spl_instruction_padding/
instruction.rs1use {
4 num_enum::{IntoPrimitive, TryFromPrimitive},
5 miraland_program::{
6 instruction::{AccountMeta, Instruction},
7 program_error::ProgramError,
8 pubkey::Pubkey,
9 syscalls::{MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_DATA_LEN},
10 },
11 std::{convert::TryInto, mem::size_of},
12};
13
14#[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)]
18#[repr(u8)]
19pub enum PadInstruction {
20 Noop,
22 Wrap,
33}
34
35pub struct WrapData<'a> {
37 pub num_accounts: u32,
39 pub instruction_size: u32,
41 pub instruction_data: &'a [u8],
43 }
45
46const U32_BYTES: usize = 4;
47fn unpack_u32(input: &[u8]) -> Result<(u32, &[u8]), ProgramError> {
48 let value = input
49 .get(..U32_BYTES)
50 .and_then(|slice| slice.try_into().ok())
51 .map(u32::from_le_bytes)
52 .ok_or(ProgramError::InvalidInstructionData)?;
53 Ok((value, &input[U32_BYTES..]))
54}
55
56impl<'a> WrapData<'a> {
57 pub fn unpack(data: &'a [u8]) -> Result<Self, ProgramError> {
59 let (num_accounts, rest) = unpack_u32(data)?;
60 let (instruction_size, rest) = unpack_u32(rest)?;
61
62 let (instruction_data, _rest) = rest.split_at(instruction_size as usize);
63 Ok(Self {
64 num_accounts,
65 instruction_size,
66 instruction_data,
67 })
68 }
69}
70
71pub fn noop(
72 program_id: Pubkey,
73 padding_accounts: Vec<AccountMeta>,
74 padding_data: u32,
75) -> Result<Instruction, ProgramError> {
76 let total_data_size = size_of::<u8>().saturating_add(padding_data as usize);
77 if total_data_size > MAX_CPI_INSTRUCTION_DATA_LEN as usize {
79 return Err(ProgramError::InvalidInstructionData);
80 }
81 let mut data = Vec::with_capacity(total_data_size);
82 data.push(PadInstruction::Noop.into());
83 for i in 0..padding_data {
84 data.push(i.checked_rem(u8::MAX as u32).unwrap() as u8);
85 }
86
87 let num_accounts = padding_accounts.len().saturating_add(1);
88 if num_accounts > MAX_CPI_ACCOUNT_INFOS {
89 return Err(ProgramError::InvalidAccountData);
90 }
91 let mut accounts = Vec::with_capacity(num_accounts);
92 accounts.extend(padding_accounts);
93
94 Ok(Instruction {
95 program_id,
96 accounts,
97 data,
98 })
99}
100
101pub fn wrap_instruction(
102 program_id: Pubkey,
103 instruction: Instruction,
104 padding_accounts: Vec<AccountMeta>,
105 padding_data: u32,
106) -> Result<Instruction, ProgramError> {
107 let total_data_size = size_of::<u8>()
108 .saturating_add(size_of::<u32>())
109 .saturating_add(size_of::<u32>())
110 .saturating_add(instruction.data.len())
111 .saturating_add(padding_data as usize);
112 if total_data_size > MAX_CPI_INSTRUCTION_DATA_LEN as usize {
114 return Err(ProgramError::InvalidInstructionData);
115 }
116 let mut data = Vec::with_capacity(total_data_size);
117 data.push(PadInstruction::Wrap.into());
118 let num_accounts: u32 = instruction
119 .accounts
120 .len()
121 .try_into()
122 .map_err(|_| ProgramError::InvalidInstructionData)?;
123 data.extend(num_accounts.to_le_bytes().iter());
124
125 let data_size: u32 = instruction
126 .data
127 .len()
128 .try_into()
129 .map_err(|_| ProgramError::InvalidInstructionData)?;
130 data.extend(data_size.to_le_bytes().iter());
131 data.extend(instruction.data);
132 for i in 0..padding_data {
133 data.push(i.checked_rem(u8::MAX as u32).unwrap() as u8);
134 }
135
136 let num_accounts = instruction
141 .accounts
142 .len()
143 .saturating_add(1)
144 .saturating_add(padding_accounts.len());
145 if num_accounts > MAX_CPI_ACCOUNT_INFOS {
146 return Err(ProgramError::InvalidAccountData);
147 }
148 let mut accounts = Vec::with_capacity(num_accounts);
149 accounts.extend(instruction.accounts);
150 accounts.push(AccountMeta::new_readonly(instruction.program_id, false));
151 accounts.extend(padding_accounts);
152
153 Ok(Instruction {
154 program_id,
155 accounts,
156 data,
157 })
158}