Skip to main content

sbpf_runtime/syscalls/
mod.rs

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
51// Consume CUs for CPI.
52fn consume_cpi_compute_units(
53    request: &CpiRequest,
54    compute: &ComputeMeter,
55    costs: &ExecutionCost,
56) -> SbpfVmResult<()> {
57    // Base invoke cost.
58    compute.consume(costs.invoke_units)?;
59
60    // Instruction data and account meta cost.
61    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    // Account info translation cost.
66    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    // Per-account data cost (skip duplicates).
71    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}