1pub mod abort;
2pub mod crypto;
3pub mod log;
4pub mod memory;
5pub mod pda;
6pub mod return_data;
7pub mod sysvar;
8
9use {
10 crate::{
11 config::{ExecutionCost, SysvarContext},
12 cpi::request::{self, CpiRequest},
13 runtime::LogCollector,
14 },
15 sbpf_vm::{
16 compute::ComputeMeter, errors::SbpfVmResult, memory::Memory, syscalls::SyscallHandler,
17 },
18 solana_address::Address,
19};
20
21const ACCOUNT_META_SIZE: u64 = 34;
22const ACCOUNT_INFO_BYTE_SIZE: u64 = 80;
23
24pub struct RuntimeSyscallHandler {
25 pub costs: ExecutionCost,
26 pub program_id: Address,
27 pub sysvars: SysvarContext,
28 pub pending_cpi: Option<CpiRequest>,
29 pub return_data: crate::cpi::ReturnData,
30 pub log_collector: LogCollector,
31}
32
33impl RuntimeSyscallHandler {
34 pub fn new(
35 costs: ExecutionCost,
36 program_id: Address,
37 sysvars: SysvarContext,
38 log_collector: LogCollector,
39 ) -> Self {
40 Self {
41 costs,
42 program_id,
43 sysvars,
44 pending_cpi: None,
45 return_data: None,
46 log_collector,
47 }
48 }
49}
50
51fn consume_cpi_compute_units(
53 request: &CpiRequest,
54 compute: &ComputeMeter,
55 costs: &ExecutionCost,
56) -> SbpfVmResult<()> {
57 compute.consume(costs.invoke_units)?;
59
60 let data_cost = request.data.len() as u64 / costs.cpi_bytes_per_unit;
62 let meta_cost = (request.accounts.len() as u64 * ACCOUNT_META_SIZE) / costs.cpi_bytes_per_unit;
63 compute.consume(data_cost + meta_cost)?;
64
65 let account_info_cost =
67 (request.caller_accounts.len() as u64 * ACCOUNT_INFO_BYTE_SIZE) / costs.cpi_bytes_per_unit;
68 compute.consume(account_info_cost)?;
69
70 let mut seen = Vec::with_capacity(request.accounts.len());
72 for meta in request.accounts.iter() {
73 if seen.contains(&meta.pubkey) {
74 continue;
75 }
76 seen.push(meta.pubkey);
77 if let Some(caller) = request
78 .caller_accounts
79 .iter()
80 .find(|c| c.pubkey == meta.pubkey)
81 {
82 let acct_cost = caller.data_len / costs.cpi_bytes_per_unit;
83 compute.consume(acct_cost)?;
84 }
85 }
86 Ok(())
87}
88
89impl SyscallHandler for RuntimeSyscallHandler {
90 fn handle(
91 &mut self,
92 name: &str,
93 registers: [u64; 5],
94 memory: &mut Memory,
95 compute: ComputeMeter,
96 ) -> SbpfVmResult<u64> {
97 match name {
98 "sol_log_" => log::sol_log(
99 registers,
100 memory,
101 &compute,
102 &self.costs,
103 &self.log_collector,
104 ),
105 "sol_log_64_" => log::sol_log_64(registers, &compute, &self.costs, &self.log_collector),
106 "sol_log_pubkey" => log::sol_log_pubkey(
107 registers,
108 memory,
109 &compute,
110 &self.costs,
111 &self.log_collector,
112 ),
113 "sol_log_compute_units_" => {
114 log::sol_log_compute_units(&compute, &self.costs, &self.log_collector)
115 }
116 "sol_remaining_compute_units" => {
117 log::sol_remaining_compute_units(&compute, &self.costs)
118 }
119
120 "sol_memcpy_" => memory::sol_memcpy(registers, memory, &compute, &self.costs),
121 "sol_memmove_" => memory::sol_memmove(registers, memory, &compute, &self.costs),
122 "sol_memset_" => memory::sol_memset(registers, memory, &compute, &self.costs),
123 "sol_memcmp_" => memory::sol_memcmp(registers, memory, &compute, &self.costs),
124
125 "abort" => abort::abort(),
126 "sol_panic_" => abort::sol_panic(registers, memory),
127
128 "sol_sha256" => crypto::sol_sha256(registers, memory, &compute, &self.costs),
129 "sol_keccak256" => crypto::sol_keccak256(registers, memory, &compute, &self.costs),
130 "sol_blake3" => crypto::sol_blake3(registers, memory, &compute, &self.costs),
131
132 "sol_create_program_address" => {
133 pda::sol_create_program_address(registers, memory, &compute, &self.costs)
134 }
135 "sol_try_find_program_address" => {
136 pda::sol_try_find_program_address(registers, memory, &compute, &self.costs)
137 }
138
139 "sol_get_clock_sysvar" => sysvar::sol_get_clock_sysvar(
140 registers,
141 memory,
142 &compute,
143 &self.costs,
144 &self.sysvars,
145 ),
146 "sol_get_rent_sysvar" => {
147 sysvar::sol_get_rent_sysvar(registers, memory, &compute, &self.costs, &self.sysvars)
148 }
149 "sol_get_epoch_schedule_sysvar" => sysvar::sol_get_epoch_schedule_sysvar(
150 registers,
151 memory,
152 &compute,
153 &self.costs,
154 &self.sysvars,
155 ),
156 "sol_get_last_restart_slot_sysvar" => sysvar::sol_get_last_restart_slot_sysvar(
157 registers,
158 memory,
159 &compute,
160 &self.costs,
161 &self.sysvars,
162 ),
163
164 "sol_set_return_data" => {
165 let (result, data) = return_data::sol_set_return_data(
166 registers,
167 memory,
168 &compute,
169 &self.costs,
170 &self.program_id,
171 )?;
172 self.return_data = data;
173 Ok(result)
174 }
175 "sol_get_return_data" => return_data::sol_get_return_data(
176 registers,
177 memory,
178 &compute,
179 &self.costs,
180 &self.return_data,
181 ),
182
183 "sol_invoke_signed_c" => {
184 let request = request::parse_cpi_c(registers, memory, &self.program_id)?;
185 consume_cpi_compute_units(&request, &compute, &self.costs)?;
186 self.pending_cpi = Some(request);
187 Ok(0)
188 }
189 "sol_invoke_signed_rust" => {
190 let request = request::parse_cpi_rust(registers, memory, &self.program_id)?;
191 consume_cpi_compute_units(&request, &compute, &self.costs)?;
192 self.pending_cpi = Some(request);
193 Ok(0)
194 }
195
196 _ => {
197 compute.consume(self.costs.syscall_base_cost)?;
198 eprintln!("Unknown syscall: {}", name);
199 Ok(0)
200 }
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use {
208 self::test_helpers::{make_memory, meter},
209 super::*,
210 crate::cpi::request::{CallerAccountInfo, CpiAccountMeta},
211 sbpf_vm::errors::SbpfVmError,
212 std::{cell::RefCell, rc::Rc},
213 };
214
215 pub(crate) mod test_helpers {
216 use {
217 crate::config::ExecutionCost,
218 sbpf_vm::{compute::ComputeMeter, memory::Memory},
219 };
220
221 pub fn make_memory() -> Memory {
222 Memory::new(vec![], vec![], 4096, 64 * 1024)
223 }
224
225 pub fn costs() -> ExecutionCost {
226 ExecutionCost::default()
227 }
228
229 pub fn meter(limit: u64) -> ComputeMeter {
230 ComputeMeter::new(limit)
231 }
232 }
233
234 const LIMIT: u64 = 1_000_000;
235
236 fn addr(seed: u8) -> Address {
237 Address::new_from_array([seed; 32])
238 }
239
240 fn meta(pubkey: Address) -> CpiAccountMeta {
241 CpiAccountMeta {
242 pubkey,
243 is_signer: false,
244 is_writable: true,
245 }
246 }
247
248 fn caller(pubkey: Address, data_len: u64) -> CallerAccountInfo {
249 CallerAccountInfo {
250 pubkey,
251 lamports_addr: 0,
252 data_addr: 0,
253 data_len,
254 owner_addr: 0,
255 vm_data_len_addr: 0,
256 is_writable: true,
257 }
258 }
259
260 fn handler() -> RuntimeSyscallHandler {
261 RuntimeSyscallHandler::new(
262 ExecutionCost::default(),
263 addr(7),
264 SysvarContext::default(),
265 Rc::new(RefCell::new(Vec::new())),
266 )
267 }
268
269 #[test]
270 fn consume_cpi_compute_units_dedups_and_charges_data() {
271 let costs = ExecutionCost::default();
272 let a = addr(1);
273 let b = addr(2);
274 let c = addr(3);
275 let request = CpiRequest {
277 program_id: addr(9),
278 accounts: vec![meta(a), meta(b), meta(c), meta(a)],
279 data: vec![0u8; 800],
280 caller_accounts: vec![caller(a, 1000), caller(b, 500)],
281 signers: Vec::new(),
282 };
283 let compute = meter(LIMIT);
284 consume_cpi_compute_units(&request, &compute, &costs).unwrap();
285
286 let expected = costs.invoke_units + 3 + 4 + 2;
287 assert_eq!(compute.get_consumed(), expected);
288 }
289
290 #[test]
291 fn consume_cpi_compute_units_no_caller_accounts() {
292 let costs = ExecutionCost::default();
293 let request = CpiRequest {
294 program_id: addr(9),
295 accounts: vec![meta(addr(1)), meta(addr(2))],
296 data: Vec::new(),
297 caller_accounts: Vec::new(),
298 signers: Vec::new(),
299 };
300 let compute = meter(LIMIT);
301 consume_cpi_compute_units(&request, &compute, &costs).unwrap();
302 assert_eq!(compute.get_consumed(), costs.invoke_units);
303 }
304
305 #[test]
306 fn consume_cpi_compute_units_out_of_budget() {
307 let costs = ExecutionCost::default();
308 let request = CpiRequest {
309 program_id: addr(9),
310 accounts: vec![meta(addr(1))],
311 data: Vec::new(),
312 caller_accounts: Vec::new(),
313 signers: Vec::new(),
314 };
315 let compute = ComputeMeter::new(10); assert!(consume_cpi_compute_units(&request, &compute, &costs).is_err());
317 }
318
319 #[test]
320 fn handle_unknown_syscall_charges_base_and_returns_zero() {
321 let mut h = handler();
322 let mut memory = make_memory();
323 let compute = meter(LIMIT);
324 let out = h
325 .handle("sol_does_not_exist", [0; 5], &mut memory, compute.clone())
326 .unwrap();
327 assert_eq!(out, 0);
328 assert_eq!(compute.get_consumed(), h.costs.syscall_base_cost);
329 }
330
331 #[test]
332 fn handle_abort_returns_abort_error() {
333 let mut h = handler();
334 let mut memory = make_memory();
335 let compute = meter(LIMIT);
336 let err = h.handle("abort", [0; 5], &mut memory, compute).unwrap_err();
337 assert!(matches!(err, SbpfVmError::Abort));
338 }
339
340 #[test]
341 fn handle_log_64_writes_log() {
342 let mut h = handler();
343 let mut memory = make_memory();
344 let compute = meter(LIMIT);
345 h.handle("sol_log_64_", [1, 2, 3, 4, 5], &mut memory, compute)
346 .unwrap();
347 assert!(!h.log_collector.borrow().is_empty());
348 }
349
350 #[test]
351 fn handle_remaining_compute_units_reports_remaining() {
352 let mut h = handler();
353 let mut memory = make_memory();
354 let compute = meter(LIMIT);
355 let out = h
356 .handle(
357 "sol_remaining_compute_units",
358 [0; 5],
359 &mut memory,
360 compute.clone(),
361 )
362 .unwrap();
363 assert_eq!(out, compute.get_remaining());
365 }
366}