clone_spl_instruction_padding/
instruction.rs1use {
4 clone_solana_instruction::{AccountMeta, Instruction},
5 clone_solana_program_error::ProgramError,
6 clone_solana_pubkey::Pubkey,
7 num_enum::{IntoPrimitive, TryFromPrimitive},
8 std::{convert::TryInto, mem::size_of},
9};
10
11const MAX_CPI_ACCOUNT_INFOS: usize = 128;
12const MAX_CPI_INSTRUCTION_DATA_LEN: u64 = 10 * 1024;
13
14#[cfg(test)]
15static_assertions::const_assert_eq!(
16 MAX_CPI_ACCOUNT_INFOS,
17 clone_solana_program::syscalls::MAX_CPI_ACCOUNT_INFOS
18);
19#[cfg(test)]
20static_assertions::const_assert_eq!(
21 MAX_CPI_INSTRUCTION_DATA_LEN,
22 clone_solana_program::syscalls::MAX_CPI_INSTRUCTION_DATA_LEN
23);
24
25#[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)]
29#[repr(u8)]
30pub enum PadInstruction {
31 Noop,
33 Wrap,
44}
45
46pub struct WrapData<'a> {
48 pub num_accounts: u32,
50 pub instruction_size: u32,
52 pub instruction_data: &'a [u8],
54 }
56
57const U32_BYTES: usize = 4;
58fn unpack_u32(input: &[u8]) -> Result<(u32, &[u8]), ProgramError> {
59 let value = input
60 .get(..U32_BYTES)
61 .and_then(|slice| slice.try_into().ok())
62 .map(u32::from_le_bytes)
63 .ok_or(ProgramError::InvalidInstructionData)?;
64 Ok((value, &input[U32_BYTES..]))
65}
66
67impl<'a> WrapData<'a> {
68 pub fn unpack(data: &'a [u8]) -> Result<Self, ProgramError> {
70 let (num_accounts, rest) = unpack_u32(data)?;
71 let (instruction_size, rest) = unpack_u32(rest)?;
72
73 let (instruction_data, _rest) = rest.split_at(instruction_size as usize);
74 Ok(Self {
75 num_accounts,
76 instruction_size,
77 instruction_data,
78 })
79 }
80}
81
82pub fn noop(
83 program_id: Pubkey,
84 padding_accounts: Vec<AccountMeta>,
85 padding_data: u32,
86) -> Result<Instruction, ProgramError> {
87 let total_data_size = size_of::<u8>().saturating_add(padding_data as usize);
88 if total_data_size > MAX_CPI_INSTRUCTION_DATA_LEN as usize {
90 return Err(ProgramError::InvalidInstructionData);
91 }
92 let mut data = Vec::with_capacity(total_data_size);
93 data.push(PadInstruction::Noop.into());
94 for i in 0..padding_data {
95 data.push(i.checked_rem(u8::MAX as u32).unwrap() as u8);
96 }
97
98 let num_accounts = padding_accounts.len().saturating_add(1);
99 if num_accounts > MAX_CPI_ACCOUNT_INFOS {
100 return Err(ProgramError::InvalidAccountData);
101 }
102 let mut accounts = Vec::with_capacity(num_accounts);
103 accounts.extend(padding_accounts);
104
105 Ok(Instruction {
106 program_id,
107 accounts,
108 data,
109 })
110}
111
112pub fn wrap_instruction(
113 program_id: Pubkey,
114 instruction: Instruction,
115 padding_accounts: Vec<AccountMeta>,
116 padding_data: u32,
117) -> Result<Instruction, ProgramError> {
118 let total_data_size = size_of::<u8>()
119 .saturating_add(size_of::<u32>())
120 .saturating_add(size_of::<u32>())
121 .saturating_add(instruction.data.len())
122 .saturating_add(padding_data as usize);
123 if total_data_size > MAX_CPI_INSTRUCTION_DATA_LEN as usize {
125 return Err(ProgramError::InvalidInstructionData);
126 }
127 let mut data = Vec::with_capacity(total_data_size);
128 data.push(PadInstruction::Wrap.into());
129 let num_accounts: u32 = instruction
130 .accounts
131 .len()
132 .try_into()
133 .map_err(|_| ProgramError::InvalidInstructionData)?;
134 data.extend(num_accounts.to_le_bytes().iter());
135
136 let data_size: u32 = instruction
137 .data
138 .len()
139 .try_into()
140 .map_err(|_| ProgramError::InvalidInstructionData)?;
141 data.extend(data_size.to_le_bytes().iter());
142 data.extend(instruction.data);
143 for i in 0..padding_data {
144 data.push(i.checked_rem(u8::MAX as u32).unwrap() as u8);
145 }
146
147 let num_accounts = instruction
152 .accounts
153 .len()
154 .saturating_add(1)
155 .saturating_add(padding_accounts.len());
156 if num_accounts > MAX_CPI_ACCOUNT_INFOS {
157 return Err(ProgramError::InvalidAccountData);
158 }
159 let mut accounts = Vec::with_capacity(num_accounts);
160 accounts.extend(instruction.accounts);
161 accounts.push(AccountMeta::new_readonly(instruction.program_id, false));
162 accounts.extend(padding_accounts);
163
164 Ok(Instruction {
165 program_id,
166 accounts,
167 data,
168 })
169}