1use {
4 crate::id,
5 solana_instruction::{AccountMeta, Instruction},
6 solana_program_error::ProgramError,
7 solana_pubkey::Pubkey,
8 std::mem::size_of,
9};
10
11#[derive(Clone, Debug, PartialEq)]
13pub enum RecordInstruction<'a> {
14 Initialize,
21
22 Write {
29 offset: u64,
31 data: &'a [u8],
33 },
34
35 SetAuthority,
43
44 CloseAccount,
53
54 Reallocate {
64 data_length: u64,
67 },
68}
69
70impl<'a> RecordInstruction<'a> {
71 pub fn unpack(input: &'a [u8]) -> Result<Self, ProgramError> {
73 const U32_BYTES: usize = 4;
74 const U64_BYTES: usize = 8;
75
76 let (&tag, rest) = input
77 .split_first()
78 .ok_or(ProgramError::InvalidInstructionData)?;
79 Ok(match tag {
80 0 => Self::Initialize,
81 1 => {
82 let offset = rest
83 .get(..U64_BYTES)
84 .and_then(|slice| slice.try_into().ok())
85 .map(u64::from_le_bytes)
86 .ok_or(ProgramError::InvalidInstructionData)?;
87 let (length, data) = rest[U64_BYTES..].split_at(U32_BYTES);
88 let length = u32::from_le_bytes(
89 length
90 .try_into()
91 .map_err(|_| ProgramError::InvalidInstructionData)?,
92 ) as usize;
93
94 Self::Write {
95 offset,
96 data: &data[..length],
97 }
98 }
99 2 => Self::SetAuthority,
100 3 => Self::CloseAccount,
101 4 => {
102 let data_length = rest
103 .get(..U64_BYTES)
104 .and_then(|slice| slice.try_into().ok())
105 .map(u64::from_le_bytes)
106 .ok_or(ProgramError::InvalidInstructionData)?;
107
108 Self::Reallocate { data_length }
109 }
110 _ => return Err(ProgramError::InvalidInstructionData),
111 })
112 }
113
114 pub fn pack(&self) -> Vec<u8> {
116 let mut buf = Vec::with_capacity(size_of::<Self>());
117 match self {
118 Self::Initialize => buf.push(0),
119 Self::Write { offset, data } => {
120 buf.push(1);
121 buf.extend_from_slice(&offset.to_le_bytes());
122 buf.extend_from_slice(&(data.len() as u32).to_le_bytes());
123 buf.extend_from_slice(data);
124 }
125 Self::SetAuthority => buf.push(2),
126 Self::CloseAccount => buf.push(3),
127 Self::Reallocate { data_length } => {
128 buf.push(4);
129 buf.extend_from_slice(&data_length.to_le_bytes());
130 }
131 };
132 buf
133 }
134}
135
136pub fn initialize(record_account: &Pubkey, authority: &Pubkey) -> Instruction {
138 Instruction {
139 program_id: id(),
140 accounts: vec![
141 AccountMeta::new(*record_account, false),
142 AccountMeta::new_readonly(*authority, false),
143 ],
144 data: RecordInstruction::Initialize.pack(),
145 }
146}
147
148pub fn write(record_account: &Pubkey, signer: &Pubkey, offset: u64, data: &[u8]) -> Instruction {
150 Instruction {
151 program_id: id(),
152 accounts: vec![
153 AccountMeta::new(*record_account, false),
154 AccountMeta::new_readonly(*signer, true),
155 ],
156 data: RecordInstruction::Write { offset, data }.pack(),
157 }
158}
159
160pub fn set_authority(
162 record_account: &Pubkey,
163 signer: &Pubkey,
164 new_authority: &Pubkey,
165) -> Instruction {
166 Instruction {
167 program_id: id(),
168 accounts: vec![
169 AccountMeta::new(*record_account, false),
170 AccountMeta::new_readonly(*signer, true),
171 AccountMeta::new_readonly(*new_authority, false),
172 ],
173 data: RecordInstruction::SetAuthority.pack(),
174 }
175}
176
177pub fn close_account(record_account: &Pubkey, signer: &Pubkey, receiver: &Pubkey) -> Instruction {
179 Instruction {
180 program_id: id(),
181 accounts: vec![
182 AccountMeta::new(*record_account, false),
183 AccountMeta::new_readonly(*signer, true),
184 AccountMeta::new(*receiver, false),
185 ],
186 data: RecordInstruction::CloseAccount.pack(),
187 }
188}
189
190pub fn reallocate(record_account: &Pubkey, signer: &Pubkey, data_length: u64) -> Instruction {
192 Instruction {
193 program_id: id(),
194 accounts: vec![
195 AccountMeta::new(*record_account, false),
196 AccountMeta::new_readonly(*signer, true),
197 ],
198 data: RecordInstruction::Reallocate { data_length }.pack(),
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use {super::*, crate::state::tests::TEST_BYTES, solana_program_error::ProgramError};
205
206 #[test]
207 fn serialize_initialize() {
208 let instruction = RecordInstruction::Initialize;
209 let expected = vec![0];
210 assert_eq!(instruction.pack(), expected);
211 assert_eq!(RecordInstruction::unpack(&expected).unwrap(), instruction);
212 }
213
214 #[test]
215 fn serialize_write() {
216 let data = &TEST_BYTES;
217 let offset = 0u64;
218 let instruction = RecordInstruction::Write { offset: 0, data };
219 let mut expected = vec![1];
220 expected.extend_from_slice(&offset.to_le_bytes());
221 expected.extend_from_slice(&(data.len() as u32).to_le_bytes());
222 expected.extend_from_slice(data);
223 assert_eq!(instruction.pack(), expected);
224 assert_eq!(RecordInstruction::unpack(&expected).unwrap(), instruction);
225 }
226
227 #[test]
228 fn serialize_set_authority() {
229 let instruction = RecordInstruction::SetAuthority;
230 let expected = vec![2];
231 assert_eq!(instruction.pack(), expected);
232 assert_eq!(RecordInstruction::unpack(&expected).unwrap(), instruction);
233 }
234
235 #[test]
236 fn serialize_close_account() {
237 let instruction = RecordInstruction::CloseAccount;
238 let expected = vec![3];
239 assert_eq!(instruction.pack(), expected);
240 assert_eq!(RecordInstruction::unpack(&expected).unwrap(), instruction);
241 }
242
243 #[test]
244 fn serialize_reallocate() {
245 let data_length = 16u64;
246 let instruction = RecordInstruction::Reallocate { data_length };
247 let mut expected = vec![4];
248 expected.extend_from_slice(&data_length.to_le_bytes());
249 assert_eq!(instruction.pack(), expected);
250 assert_eq!(RecordInstruction::unpack(&expected).unwrap(), instruction);
251 }
252
253 #[test]
254 fn deserialize_invalid_instruction() {
255 let mut expected = vec![12];
256 expected.extend_from_slice(&TEST_BYTES);
257 let err: ProgramError = RecordInstruction::unpack(&expected).unwrap_err();
258 assert_eq!(err, ProgramError::InvalidInstructionData);
259 }
260}