batch.rs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. use {
  2. crate::{entrypoint::inner_process_instruction, processor::check_account_owner},
  3. pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult},
  4. pinocchio_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. // Few Instructions require specific account ownership checks when executed
  41. // in a batch since ownership is only enforced by the runtime at the end of
  42. // the batch processing.
  43. //
  44. // Instructions that do not appear in the list below do not require
  45. // ownership checks since they either do not modify accounts or the ownership
  46. // is already checked explicitly.
  47. if let Some(&discriminator) = ix_data.first() {
  48. match discriminator {
  49. // 3 - Transfer
  50. // 7 - MintTo
  51. // 8 - Burn
  52. // 14 - MintToChecked
  53. // 15 - BurnChecked
  54. 3 | 7 | 8 | 14 | 15 => {
  55. let [a0, a1, ..] = ix_accounts else {
  56. return Err(ProgramError::NotEnoughAccountKeys);
  57. };
  58. check_account_owner(a0)?;
  59. check_account_owner(a1)?;
  60. }
  61. // 12 - TransferChecked
  62. 12 => {
  63. let [a0, _, a2, ..] = ix_accounts else {
  64. return Err(ProgramError::NotEnoughAccountKeys);
  65. };
  66. check_account_owner(a0)?;
  67. check_account_owner(a2)?;
  68. }
  69. // 4 - Approve
  70. // 5 - Revoke
  71. // 6 - SetAuthority
  72. // 9 - CloseAccount
  73. // 10 - FreezeAccount
  74. // 11 - ThawAccount
  75. // 13 - ApproveChecked
  76. // 22 - InitializeImmutableOwner
  77. // 38 - WithdrawExcessLamports
  78. // 45 - UnwrapLamports
  79. 4..=13 | 22 | 38 | 45 => {
  80. let [a0, ..] = ix_accounts else {
  81. return Err(ProgramError::NotEnoughAccountKeys);
  82. };
  83. check_account_owner(a0)?;
  84. }
  85. _ => {}
  86. }
  87. }
  88. inner_process_instruction(ix_accounts, ix_data)?;
  89. if data_offset == instruction_data.len() {
  90. // The batch is complete.
  91. break;
  92. }
  93. accounts = &accounts[expected_accounts..];
  94. instruction_data = &instruction_data[data_offset..];
  95. }
  96. Ok(())
  97. }