1use {
2 sbpf_vm::{errors::SbpfVmResult, memory::Memory},
3 solana_address::Address,
4};
5
6const MAX_SIGNERS: u64 = 16;
7const MAX_SEEDS: u64 = 16;
8const MAX_SEED_LEN: u64 = 32;
9
10pub struct CpiRequest {
11 pub program_id: Address,
12 pub accounts: Vec<CpiAccountMeta>,
13 pub data: Vec<u8>,
14 pub caller_accounts: Vec<CallerAccountInfo>,
15 pub signers: Vec<Address>,
16}
17
18pub struct CpiAccountMeta {
19 pub pubkey: Address,
20 pub is_signer: bool,
21 pub is_writable: bool,
22}
23
24pub struct CallerAccountInfo {
26 pub pubkey: Address,
27 pub lamports_addr: u64,
28 pub data_addr: u64,
29 pub data_len: u64,
30 pub owner_addr: u64,
31 pub vm_data_len_addr: u64,
32 pub is_writable: bool,
33}
34
35pub fn parse_cpi_c(
37 registers: [u64; 5],
38 memory: &Memory,
39 caller_program_id: &Address,
40) -> SbpfVmResult<CpiRequest> {
41 let instruction_addr = registers[0];
42 let account_infos_addr = registers[1];
43 let account_infos_len = registers[2];
44 let signers_seeds_addr = registers[3];
45 let signers_seeds_len = registers[4];
46
47 let program_id_ptr = memory.read_u64(instruction_addr)?;
48 let accounts_ptr = memory.read_u64(instruction_addr + 8)?;
49 let accounts_len = memory.read_u64(instruction_addr + 16)?;
50 let data_ptr = memory.read_u64(instruction_addr + 24)?;
51 let data_len = memory.read_u64(instruction_addr + 32)?;
52
53 let program_id_bytes = memory.read_bytes(program_id_ptr, 32)?;
54 let program_id = Address::new_from_array(program_id_bytes.try_into().unwrap());
55
56 let mut accounts = Vec::with_capacity(accounts_len as usize);
57 for i in 0..accounts_len {
58 let meta_addr = accounts_ptr + i * 16;
59 let pubkey_ptr = memory.read_u64(meta_addr)?;
60 let pubkey_bytes = memory.read_bytes(pubkey_ptr, 32)?;
61 let is_writable = memory.read_u8(meta_addr + 8)? != 0;
62 let is_signer = memory.read_u8(meta_addr + 9)? != 0;
63 accounts.push(CpiAccountMeta {
64 pubkey: Address::new_from_array(pubkey_bytes.try_into().unwrap()),
65 is_signer,
66 is_writable,
67 });
68 }
69
70 let data = memory.read_bytes(data_ptr, data_len as usize)?.to_vec();
71
72 let caller_accounts = parse_account_infos_c(memory, account_infos_addr, account_infos_len)?;
73
74 let signers = parse_signers(
75 memory,
76 caller_program_id,
77 signers_seeds_addr,
78 signers_seeds_len,
79 )?;
80
81 Ok(CpiRequest {
82 program_id,
83 accounts,
84 data,
85 caller_accounts,
86 signers,
87 })
88}
89
90pub fn parse_cpi_rust(
92 registers: [u64; 5],
93 memory: &Memory,
94 caller_program_id: &Address,
95) -> SbpfVmResult<CpiRequest> {
96 let instruction_addr = registers[0];
97 let account_infos_addr = registers[1];
98 let account_infos_len = registers[2];
99 let signers_seeds_addr = registers[3];
100 let signers_seeds_len = registers[4];
101
102 let accounts_ptr = memory.read_u64(instruction_addr)?;
103 let _accounts_cap = memory.read_u64(instruction_addr + 8)?;
104 let accounts_len = memory.read_u64(instruction_addr + 16)?;
105 let data_ptr = memory.read_u64(instruction_addr + 24)?;
106 let _data_cap = memory.read_u64(instruction_addr + 32)?;
107 let data_len = memory.read_u64(instruction_addr + 40)?;
108 let program_id_bytes = memory.read_bytes(instruction_addr + 48, 32)?;
109 let program_id = Address::new_from_array(program_id_bytes.try_into().unwrap());
110
111 let mut accounts = Vec::with_capacity(accounts_len as usize);
112 for i in 0..accounts_len {
113 let meta_addr = accounts_ptr + i * 34;
114 let pubkey_bytes = memory.read_bytes(meta_addr, 32)?;
115 let is_signer = memory.read_u8(meta_addr + 32)? != 0;
116 let is_writable = memory.read_u8(meta_addr + 33)? != 0;
117 accounts.push(CpiAccountMeta {
118 pubkey: Address::new_from_array(pubkey_bytes.try_into().unwrap()),
119 is_signer,
120 is_writable,
121 });
122 }
123
124 let data = memory.read_bytes(data_ptr, data_len as usize)?.to_vec();
125 let caller_accounts = parse_account_infos_rust(memory, account_infos_addr, account_infos_len)?;
126 let signers = parse_signers(
127 memory,
128 caller_program_id,
129 signers_seeds_addr,
130 signers_seeds_len,
131 )?;
132
133 Ok(CpiRequest {
134 program_id,
135 accounts,
136 data,
137 caller_accounts,
138 signers,
139 })
140}
141
142fn parse_account_infos_c(
144 memory: &Memory,
145 account_infos_addr: u64,
146 account_infos_len: u64,
147) -> SbpfVmResult<Vec<CallerAccountInfo>> {
148 let mut caller_accounts = Vec::with_capacity(account_infos_len as usize);
149 for i in 0..account_infos_len {
150 let info_addr = account_infos_addr + i * 56;
151 let key_ptr = memory.read_u64(info_addr)?;
152 let lamports_ptr = memory.read_u64(info_addr + 8)?;
153 let acct_data_len = memory.read_u64(info_addr + 16)?;
154 let data_ptr = memory.read_u64(info_addr + 24)?;
155 let owner_ptr = memory.read_u64(info_addr + 32)?;
156 let _rent_epoch = memory.read_u64(info_addr + 40)?;
157 let _is_signer = memory.read_u8(info_addr + 48)? != 0;
158 let is_writable = memory.read_u8(info_addr + 49)? != 0;
159
160 let key_bytes = memory.read_bytes(key_ptr, 32)?;
161 caller_accounts.push(CallerAccountInfo {
162 pubkey: Address::new_from_array(key_bytes.try_into().unwrap()),
163 lamports_addr: lamports_ptr,
164 data_addr: data_ptr,
165 data_len: acct_data_len,
166 owner_addr: owner_ptr,
167 vm_data_len_addr: info_addr + 16,
168 is_writable,
169 });
170 }
171 Ok(caller_accounts)
172}
173
174fn parse_account_infos_rust(
176 memory: &Memory,
177 account_infos_addr: u64,
178 account_infos_len: u64,
179) -> SbpfVmResult<Vec<CallerAccountInfo>> {
180 const RUST_ACCOUNT_INFO_SIZE: u64 = 48;
181
182 let mut caller_accounts = Vec::with_capacity(account_infos_len as usize);
183 for i in 0..account_infos_len {
184 let info_addr = account_infos_addr + i * RUST_ACCOUNT_INFO_SIZE;
185
186 let key_ptr = memory.read_u64(info_addr)?;
187 let key_bytes = memory.read_bytes(key_ptr, 32)?;
188
189 let lamports_rc_ptr = memory.read_u64(info_addr + 8)?;
190 let lamports_addr = memory.read_u64(lamports_rc_ptr + 24)?;
191
192 let data_rc_ptr = memory.read_u64(info_addr + 16)?;
193 let data_addr = memory.read_u64(data_rc_ptr + 24)?;
194 let data_len = memory.read_u64(data_rc_ptr + 32)?;
195
196 let vm_data_len_addr = data_rc_ptr + 32;
197
198 let owner_ptr = memory.read_u64(info_addr + 24)?;
199 let _is_signer = memory.read_u8(info_addr + 40)? != 0;
200 let is_writable = memory.read_u8(info_addr + 41)? != 0;
201
202 caller_accounts.push(CallerAccountInfo {
203 pubkey: Address::new_from_array(key_bytes.try_into().unwrap()),
204 lamports_addr,
205 data_addr,
206 data_len,
207 owner_addr: owner_ptr,
208 vm_data_len_addr,
209 is_writable,
210 });
211 }
212 Ok(caller_accounts)
213}
214
215fn parse_signers(
217 memory: &Memory,
218 caller_program_id: &Address,
219 signers_seeds_addr: u64,
220 signers_seeds_len: u64,
221) -> SbpfVmResult<Vec<Address>> {
222 if signers_seeds_len == 0 {
223 return Ok(Vec::new());
224 }
225 if signers_seeds_len > MAX_SIGNERS {
226 return Err(sbpf_vm::errors::SbpfVmError::SyscallError(
227 "Too many signers".to_string(),
228 ));
229 }
230
231 let mut signers = Vec::with_capacity(signers_seeds_len as usize);
232
233 for i in 0..signers_seeds_len {
234 let entry_addr = signers_seeds_addr + i * 16;
235 let seeds_ptr = memory.read_u64(entry_addr)?;
236 let seeds_len = memory.read_u64(entry_addr + 8)?;
237
238 if seeds_len > MAX_SEEDS {
239 return Err(sbpf_vm::errors::SbpfVmError::SyscallError(
240 "Max seed length exceeded".to_string(),
241 ));
242 }
243
244 let mut seeds: Vec<Vec<u8>> = Vec::with_capacity(seeds_len as usize);
245 for j in 0..seeds_len {
246 let seed_entry_addr = seeds_ptr + j * 16;
247 let seed_data_ptr = memory.read_u64(seed_entry_addr)?;
248 let seed_data_len = memory.read_u64(seed_entry_addr + 8)?;
249
250 if seed_data_len > MAX_SEED_LEN {
251 return Err(sbpf_vm::errors::SbpfVmError::SyscallError(
252 "Max seed length exceeded".to_string(),
253 ));
254 }
255
256 let seed_bytes = memory.read_bytes(seed_data_ptr, seed_data_len as usize)?;
257 seeds.push(seed_bytes.to_vec());
258 }
259
260 let seed_refs: Vec<&[u8]> = seeds.iter().map(|s| s.as_slice()).collect();
261 let pda = Address::create_program_address(&seed_refs, caller_program_id)
262 .map_err(|_| sbpf_vm::errors::SbpfVmError::SyscallError("Invalid seeds".to_string()))?;
263
264 signers.push(pda);
265 }
266
267 Ok(signers)
268}