Skip to main content

sbpf_runtime/cpi/
request.rs

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
24/// Caller's VM memory pointers for an account.
25pub 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
35/// Parse sol_invoke_signed_c arguments from memory.
36pub 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
90/// Parse sol_invoke_signed_rust arguments from memory.
91pub 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
142/// Parse C `SolAccountInfo` array.
143fn 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
174/// Parse Rust `AccountInfo` array.
175fn 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
215/// Parse signers.
216fn 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}