1use crate::error::BitokuError::InvalidInstruction;
2use borsh::{BorshDeserialize, BorshSerialize};
3use solana_program::instruction::{AccountMeta, Instruction};
4use solana_program::msg;
5use solana_program::program_error::ProgramError;
6use solana_program::pubkey::Pubkey;
7use std::mem::size_of;
8
9#[repr(C)]
10#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
11pub enum Request {
12 CreateBucket { name: [u8; 128] },
13 CreateFile { name: [u8; 128] },
14 WriteFile { name: [u8; 128], file_id: u8 },
15 CloseFile { name: [u8; 128], file_id: u8 },
16 DeleteFile { name: [u8; 128], file_id: u8 },
17 SetPosition { name: [u8; 128], file_id: u8 },
18 OpenFile { name: [u8; 128], file_id: u8 },
19 ReadFile { name: [u8; 128], file_id: u8 },
20}
21
22#[derive(BorshSerialize,BorshDeserialize,Debug, Clone)]
23#[rustfmt::skip]
24pub enum BitokuInstructions {
25 InitBitoku,
30 RegisterClient,
36
37 RemoveClient{client_id:u8},
41
42 SendRequest{request : Request}
45}
46
47impl BitokuInstructions {
48 pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
49 let (tag, rest) = input.split_first().ok_or(InvalidInstruction)?;
50
51 Ok(match tag {
52 0 => Self::InitBitoku {},
53 1 => Self::RegisterClient {},
54 2 => Self::RemoveClient {
55 client_id: unpack_id(rest)?,
56 },
57 3 => Self::SendRequest {
58 request: unpack_request(rest)?,
59 },
60 _ => return Err(InvalidInstruction.into()),
61 })
62 }
63
64 pub fn pack(&self) -> Vec<u8> {
65 let mut buf = Vec::with_capacity(size_of::<Self>());
66 match self {
67 Self::InitBitoku => {
68 buf.push(0);
69 }
70 Self::RegisterClient => {
71 buf.push(1);
72 }
73
74 Self::RemoveClient { client_id } => {
75 buf.push(2);
76 buf.extend_from_slice(&client_id.to_le_bytes());
77 }
78 Self::SendRequest { request } => {
79 buf.push(3);
80 match request {
81 Request::CreateBucket { name } => {
82 buf.push(0);
83 buf.extend_from_slice(name);
84 }
85 Request::CreateFile { name } => {
86 buf.push(1);
87 buf.extend_from_slice(name);
88 }
89 Request::WriteFile { name, file_id } => {
90 buf.push(2);
91 buf.extend_from_slice(name);
92 buf.extend_from_slice(&file_id.to_le_bytes());
93 }
94 Request::CloseFile { name, file_id } => {
95 buf.push(3);
96 buf.extend_from_slice(name);
97 buf.extend_from_slice(&file_id.to_le_bytes());
98 }
99 Request::DeleteFile { name, file_id } => {
100 buf.push(4);
101 buf.extend_from_slice(name);
102 buf.extend_from_slice(&file_id.to_le_bytes());
103 }
104 Request::SetPosition { name, file_id } => {
105 buf.push(5);
106 buf.extend_from_slice(name);
107 buf.extend_from_slice(&file_id.to_le_bytes());
108 }
109 Request::OpenFile { name, file_id } => {
110 buf.push(6);
111 buf.extend_from_slice(name);
112 buf.extend_from_slice(&file_id.to_le_bytes());
113 }
114 Request::ReadFile { name, file_id } => {
115 buf.push(7);
116 buf.extend_from_slice(name);
117 buf.extend_from_slice(&file_id.to_le_bytes());
118 }
119 }
120 }
121 };
122 buf
123 }
124}
125
126pub fn unpack_request(input: &[u8]) -> Result<Request, ProgramError> {
127 let (req, data) = input.split_first().ok_or(InvalidInstruction)?;
128
129 let name = unpack_name(data)?;
130
131 Ok(match req {
132 0 => self::Request::CreateBucket { name },
133 1 => self::Request::CreateFile { name },
134 2 => self::Request::WriteFile {
135 name,
136 file_id: unpack_file_id(data)?,
137 },
138 3 => self::Request::CloseFile {
139 name,
140 file_id: unpack_file_id(data)?,
141 },
142 4 => self::Request::DeleteFile {
143 name,
144 file_id: unpack_file_id(data)?,
145 },
146 5 => self::Request::SetPosition {
147 name,
148 file_id: unpack_file_id(data)?,
149 },
150 6 => self::Request::OpenFile {
151 name,
152 file_id: unpack_file_id(data)?,
153 },
154 7 => self::Request::ReadFile {
155 name,
156 file_id: unpack_file_id(data)?,
157 },
158 _ => return Err(InvalidInstruction.into()),
159 })
160}
161
162fn unpack_id(input: &[u8]) -> Result<u8, ProgramError> {
163 let id = input
164 .get(..1)
165 .and_then(|slice| slice.try_into().ok())
166 .map(u8::from_be_bytes)
167 .ok_or(InvalidInstruction)?;
168 Ok(id)
169}
170
171fn unpack_name(input: &[u8]) -> Result<[u8; 128], ProgramError> {
172 let name = input
173 .get(..128)
174 .and_then(|slice| slice.try_into().ok())
175 .unwrap();
176 Ok(name)
177}
178
179fn unpack_file_id(input: &[u8]) -> Result<u8, ProgramError> {
180 let id = input
181 .get(128..129)
182 .and_then(|slice| slice.try_into().ok())
183 .map(u8::from_be_bytes)
184 .ok_or(InvalidInstruction)?;
185
186 Ok(id)
187}
188
189impl Request {
190 pub fn name(&self) -> [u8; 128] {
191 match self {
192 Request::CreateBucket { name } => *name,
193 Request::CreateFile { name } => *name,
194 Request::WriteFile { name, .. } => *name,
195 Request::DeleteFile { name, .. } => *name,
196 Request::CloseFile { name, .. } => *name,
197 Request::SetPosition { name, .. } => *name,
198 Request::OpenFile { name, .. } => *name,
199 Request::ReadFile { name, .. } => *name,
200 }
201 }
202}
203
204pub fn register_client(
205 fee_payer: Pubkey,
206 bookkeeper: Pubkey,
207 request: Pubkey,
208 system_program: Pubkey,
209 rent_sys_var: Pubkey,
210 bitoku_agnet_program: Pubkey,
211) -> Result<Instruction, ProgramError> {
212 let data = BitokuInstructions::RegisterClient.pack();
213
214 let accounts = vec![
215 AccountMeta::new(fee_payer, true),
216 AccountMeta::new(bookkeeper, false),
217 AccountMeta::new(request, false),
218 AccountMeta::new_readonly(system_program, false),
219 AccountMeta::new_readonly(rent_sys_var, false),
220 ];
221
222 Ok(Instruction {
223 program_id: bitoku_agnet_program,
224 accounts,
225 data,
226 })
227}
228
229pub fn remove_client(
230 fee_payer: Pubkey,
231 bookkeeper: Pubkey,
232 request: Pubkey,
233 bitoku_agnet_program: Pubkey,
234 client_id: u8,
235) -> Result<Instruction, ProgramError> {
236 let data = BitokuInstructions::RemoveClient { client_id }.pack();
237
238 let accounts = vec![
239 AccountMeta::new(fee_payer, true),
240 AccountMeta::new(bookkeeper, false),
241 AccountMeta::new(request, false),
242 ];
243
244 Ok(Instruction {
245 program_id: bitoku_agnet_program,
246 accounts,
247 data,
248 })
249}
250
251pub fn send_request(
252 fee_payer: Pubkey,
253 request: Pubkey,
254 bitoku_agnet_program: Pubkey,
255 req: Request,
256) -> Result<Instruction, ProgramError> {
257 let data = BitokuInstructions::SendRequest { request: req }.pack();
258
259 let accounts = vec![
260 AccountMeta::new(fee_payer, true),
261 AccountMeta::new(request, false),
262 ];
263
264 Ok(Instruction {
265 program_id: bitoku_agnet_program,
266 accounts,
267 data,
268 })
269}