batch.rs 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. use {
  2. crate::entrypoint::inner_process_instruction,
  3. pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult},
  4. spl_token_interface::error::TokenError,
  5. };
  6. /// The size of the batch instruction header.
  7. ///
  8. /// The header of each instruction consists of two `u8` values:
  9. /// * number of the accounts
  10. /// * length of the instruction data
  11. const IX_HEADER_SIZE: usize = 2;
  12. #[allow(clippy::arithmetic_side_effects)]
  13. pub fn process_batch(mut accounts: &[AccountInfo], mut instruction_data: &[u8]) -> ProgramResult {
  14. loop {
  15. // Validates the instruction data and accounts offset.
  16. if instruction_data.len() < IX_HEADER_SIZE {
  17. // The instruction data must have at least two bytes.
  18. return Err(TokenError::InvalidInstruction.into());
  19. }
  20. // SAFETY: The instruction data is guaranteed to have at least two bytes
  21. // (header) + one byte (discriminator) and the values are within the bounds
  22. // of an `usize`.
  23. let expected_accounts = unsafe { *instruction_data.get_unchecked(0) as usize };
  24. let data_offset = IX_HEADER_SIZE + unsafe { *instruction_data.get_unchecked(1) as usize };
  25. if instruction_data.len() < data_offset || data_offset == IX_HEADER_SIZE {
  26. return Err(TokenError::InvalidInstruction.into());
  27. }
  28. if accounts.len() < expected_accounts {
  29. return Err(ProgramError::NotEnoughAccountKeys);
  30. }
  31. // Process the instruction.
  32. // SAFETY: The instruction data and accounts lengths are already validated so
  33. // all slices are guaranteed to be valid.
  34. let (ix_accounts, ix_data) = unsafe {
  35. (
  36. accounts.get_unchecked(..expected_accounts),
  37. instruction_data.get_unchecked(IX_HEADER_SIZE..data_offset),
  38. )
  39. };
  40. inner_process_instruction(ix_accounts, ix_data)?;
  41. if data_offset == instruction_data.len() {
  42. // The batch is complete.
  43. break;
  44. }
  45. accounts = &accounts[expected_accounts..];
  46. instruction_data = &instruction_data[data_offset..];
  47. }
  48. Ok(())
  49. }