bitoku_sdk_agent_native/
instruction.rs

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    ///0. `[signer]` fee_payer account
26    /// 1. `[writable]` bookkeeper PDA account
27    /// 2.`[]` system_program account
28    /// 3.`[]` sys_var program
29    InitBitoku,
30    ///0. `[signer]` fee_payer account
31    /// 1. `[writable]` bookkeeper PDA account
32    /// 2. `[]` request Pda account
33    /// 3.`[]` system_program account
34    ///  4.`[]` sys_var program
35    RegisterClient,
36
37   ///0. `[signer]` fee_payer account
38    /// 1. `[writable]` bookkeeper PDA account
39    /// 2. `[writable]` request Pda account
40    RemoveClient{client_id:u8},
41
42    ///0. `[signer]` fee_payer account
43    /// 2. `[writable]` request Pda account
44    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}