instructions.rs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. use crate::{
  2. account_info::{AccountInfo, Ref},
  3. instruction::AccountMeta,
  4. program_error::ProgramError,
  5. pubkey::{Pubkey, PUBKEY_BYTES},
  6. };
  7. use core::{marker::PhantomData, mem::size_of, ops::Deref};
  8. /// Instructions sysvar ID `Sysvar1nstructions1111111111111111111111111`.
  9. pub const INSTRUCTIONS_ID: Pubkey = [
  10. 0x06, 0xa7, 0xd5, 0x17, 0x18, 0x7b, 0xd1, 0x66, 0x35, 0xda, 0xd4, 0x04, 0x55, 0xfd, 0xc2, 0xc0,
  11. 0xc1, 0x24, 0xc6, 0x8f, 0x21, 0x56, 0x75, 0xa5, 0xdb, 0xba, 0xcb, 0x5f, 0x08, 0x00, 0x00, 0x00,
  12. ];
  13. pub struct Instructions<T>
  14. where
  15. T: Deref<Target = [u8]>,
  16. {
  17. data: T,
  18. }
  19. impl<T> Instructions<T>
  20. where
  21. T: Deref<Target = [u8]>,
  22. {
  23. /// Creates a new `Instructions` struct.
  24. ///
  25. /// `data` is the instructions sysvar account data.
  26. ///
  27. /// # Safety
  28. ///
  29. /// This function is unsafe because it does not check if the provided data is from the Sysvar Account.
  30. #[inline(always)]
  31. pub unsafe fn new_unchecked(data: T) -> Self {
  32. Instructions { data }
  33. }
  34. /// Load the number of instructions in the currently executing `Transaction`.
  35. #[inline(always)]
  36. pub fn num_instructions(&self) -> u16 {
  37. // SAFETY: The first 2 bytes of the Instructions sysvar data represents the
  38. // number of instructions.
  39. unsafe { u16::from_le_bytes(*(self.data.as_ptr() as *const [u8; 2])) }
  40. }
  41. /// Load the current `Instruction`'s index in the currently executing
  42. /// `Transaction`.
  43. #[inline(always)]
  44. pub fn load_current_index(&self) -> u16 {
  45. let len = self.data.len();
  46. // SAFETY: The last 2 bytes of the Instructions sysvar data represents the current
  47. // instruction index.
  48. unsafe { u16::from_le_bytes(*(self.data.as_ptr().add(len - 2) as *const [u8; 2])) }
  49. }
  50. /// Creates and returns an `IntrospectedInstruction` for the instruction at the specified index.
  51. ///
  52. /// # Safety
  53. ///
  54. /// This function is unsafe because it does not check if the provided index is out of bounds. It is
  55. /// typically used internally with the `load_instruction_at` or `get_instruction_relative` functions,
  56. /// which perform the necessary index verification.
  57. #[inline(always)]
  58. pub unsafe fn deserialize_instruction_unchecked(
  59. &self,
  60. index: usize,
  61. ) -> IntrospectedInstruction {
  62. let offset = *(self
  63. .data
  64. .as_ptr()
  65. .add(size_of::<u16>() + index * size_of::<u16>()) as *const u16);
  66. IntrospectedInstruction {
  67. raw: self.data.as_ptr().add(offset as usize),
  68. marker: PhantomData,
  69. }
  70. }
  71. /// Creates and returns an `IntrospectedInstruction` for the instruction at the specified index.
  72. #[inline(always)]
  73. pub fn load_instruction_at(
  74. &self,
  75. index: usize,
  76. ) -> Result<IntrospectedInstruction, ProgramError> {
  77. if index >= self.num_instructions() as usize {
  78. return Err(ProgramError::InvalidInstructionData);
  79. }
  80. // SAFETY: The index was checked to be in bounds.
  81. Ok(unsafe { self.deserialize_instruction_unchecked(index) })
  82. }
  83. /// Creates and returns an `IntrospectedInstruction` relative to the current `Instruction` in the
  84. /// currently executing `Transaction.
  85. #[inline(always)]
  86. pub fn get_instruction_relative(
  87. &self,
  88. index_relative_to_current: i64,
  89. ) -> Result<IntrospectedInstruction, ProgramError> {
  90. let current_index = self.load_current_index() as i64;
  91. let index = current_index.saturating_add(index_relative_to_current);
  92. if index < 0 {
  93. return Err(ProgramError::InvalidInstructionData);
  94. }
  95. self.load_instruction_at(index as usize)
  96. }
  97. }
  98. impl<'a> TryFrom<&'a AccountInfo> for Instructions<Ref<'a, [u8]>> {
  99. type Error = ProgramError;
  100. #[inline(always)]
  101. fn try_from(account_info: &'a AccountInfo) -> Result<Self, Self::Error> {
  102. if account_info.key() != &INSTRUCTIONS_ID {
  103. return Err(ProgramError::UnsupportedSysvar);
  104. }
  105. Ok(Instructions {
  106. data: account_info.try_borrow_data()?,
  107. })
  108. }
  109. }
  110. #[repr(C)]
  111. #[derive(Clone, PartialEq, Eq)]
  112. pub struct IntrospectedInstruction<'a> {
  113. pub raw: *const u8,
  114. pub marker: PhantomData<&'a [u8]>,
  115. }
  116. impl IntrospectedInstruction<'_> {
  117. /// Get the account meta at the specified index.
  118. ///
  119. /// # Safety
  120. ///
  121. /// This function is unsafe because it does not verify if the index is out of bounds.
  122. ///
  123. /// It is typically used internally within the `get_account_meta_at` function, which
  124. /// performs the necessary index verification. However, to optimize performance for users
  125. /// who are sure that the index is in bounds, we have exposed it as an unsafe function.
  126. #[inline(always)]
  127. pub unsafe fn get_account_meta_at_unchecked(&self, index: usize) -> &IntrospectedAccountMeta {
  128. let offset = core::mem::size_of::<u16>() + (index * IntrospectedAccountMeta::LEN);
  129. &*(self.raw.add(offset) as *const IntrospectedAccountMeta)
  130. }
  131. /// Get the account meta at the specified index.
  132. ///
  133. /// # Errors
  134. ///
  135. /// Returns [`ProgramError::InvalidArgument`] if the index is out of bounds.
  136. #[inline(always)]
  137. pub fn get_account_meta_at(
  138. &self,
  139. index: usize,
  140. ) -> Result<&IntrospectedAccountMeta, ProgramError> {
  141. // SAFETY: The first 2 bytes represent the number of accounts in the instruction.
  142. let num_accounts = u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) });
  143. if index >= num_accounts as usize {
  144. return Err(ProgramError::InvalidArgument);
  145. }
  146. // SAFETY: The index was checked to be in bounds.
  147. Ok(unsafe { self.get_account_meta_at_unchecked(index) })
  148. }
  149. /// Get the program ID of the `Instruction`.
  150. #[inline(always)]
  151. pub fn get_program_id(&self) -> &Pubkey {
  152. // SAFETY: The first 2 bytes represent the number of accounts in the instruction.
  153. let num_accounts = u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) });
  154. // SAFETY: The program ID is located after the account metas.
  155. unsafe {
  156. &*(self.raw.add(
  157. size_of::<u16>() + num_accounts as usize * size_of::<IntrospectedAccountMeta>(),
  158. ) as *const Pubkey)
  159. }
  160. }
  161. /// Get the instruction data of the `Instruction`.
  162. #[inline(always)]
  163. pub fn get_instruction_data(&self) -> &[u8] {
  164. // SAFETY: The first 2 bytes represent the number of accounts in the instruction.
  165. let offset = u16::from_le_bytes(unsafe { *(self.raw as *const [u8; 2]) }) as usize
  166. * size_of::<IntrospectedAccountMeta>()
  167. + PUBKEY_BYTES;
  168. // SAFETY: The instruction data length is located after the program ID.
  169. let data_len = u16::from_le_bytes(unsafe {
  170. *(self.raw.add(size_of::<u16>() + offset) as *const [u8; 2])
  171. });
  172. // SAFETY: The instruction data is located after the data length.
  173. unsafe {
  174. core::slice::from_raw_parts(
  175. self.raw.add(size_of::<u16>() + offset + size_of::<u16>()),
  176. data_len as usize,
  177. )
  178. }
  179. }
  180. }
  181. /// The bit positions for the signer flags in the `AccountMeta`.
  182. const IS_SIGNER: u8 = 0b00000001;
  183. /// The bit positions for the writable flags in the `AccountMeta`.
  184. const IS_WRITABLE: u8 = 0b00000010;
  185. #[repr(C)]
  186. #[derive(Clone, PartialEq, Eq)]
  187. pub struct IntrospectedAccountMeta {
  188. /// Account flags:
  189. /// * bit `0`: signer
  190. /// * bit `1`: writable
  191. flags: u8,
  192. /// The account key.
  193. pub key: Pubkey,
  194. }
  195. impl IntrospectedAccountMeta {
  196. const LEN: usize = core::mem::size_of::<Self>();
  197. /// Indicate whether the account is writable or not.
  198. #[inline(always)]
  199. pub fn is_writable(&self) -> bool {
  200. (self.flags & IS_WRITABLE) != 0
  201. }
  202. /// Indicate whether the account is a signer or not.
  203. #[inline(always)]
  204. pub fn is_signer(&self) -> bool {
  205. (self.flags & IS_SIGNER) != 0
  206. }
  207. /// Convert the `IntrospectedAccountMeta` to an `AccountMeta`.
  208. #[inline(always)]
  209. pub fn to_account_meta(&self) -> AccountMeta {
  210. AccountMeta::new(&self.key, self.is_writable(), self.is_signer())
  211. }
  212. }