solana_bpf_loader_program/syscalls/
sysvar.rs

1use super::*;
2
3fn get_sysvar<T: std::fmt::Debug + Sysvar + SysvarId + Clone>(
4    sysvar: Result<Arc<T>, InstructionError>,
5    var_addr: u64,
6    check_aligned: bool,
7    memory_mapping: &mut MemoryMapping,
8    invoke_context: &mut InvokeContext,
9) -> Result<u64, Error> {
10    consume_compute_meter(
11        invoke_context,
12        invoke_context
13            .get_compute_budget()
14            .sysvar_base_cost
15            .saturating_add(size_of::<T>() as u64),
16    )?;
17    let var = translate_type_mut::<T>(memory_mapping, var_addr, check_aligned)?;
18
19    // this clone looks unecessary now, but it exists to zero out trailing alignment bytes
20    // it is unclear whether this should ever matter
21    // but there are tests using MemoryMapping that expect to see this
22    // we preserve the previous behavior out of an abundance of caution
23    let sysvar: Arc<T> = sysvar?;
24    *var = T::clone(sysvar.as_ref());
25
26    Ok(SUCCESS)
27}
28
29declare_builtin_function!(
30    /// Get a Clock sysvar
31    SyscallGetClockSysvar,
32    fn rust(
33        invoke_context: &mut InvokeContext,
34        var_addr: u64,
35        _arg2: u64,
36        _arg3: u64,
37        _arg4: u64,
38        _arg5: u64,
39        memory_mapping: &mut MemoryMapping,
40    ) -> Result<u64, Error> {
41        get_sysvar(
42            invoke_context.get_sysvar_cache().get_clock(),
43            var_addr,
44            invoke_context.get_check_aligned(),
45            memory_mapping,
46            invoke_context,
47        )
48    }
49);
50
51declare_builtin_function!(
52    /// Get a EpochSchedule sysvar
53    SyscallGetEpochScheduleSysvar,
54    fn rust(
55        invoke_context: &mut InvokeContext,
56        var_addr: u64,
57        _arg2: u64,
58        _arg3: u64,
59        _arg4: u64,
60        _arg5: u64,
61        memory_mapping: &mut MemoryMapping,
62    ) -> Result<u64, Error> {
63        get_sysvar(
64            invoke_context.get_sysvar_cache().get_epoch_schedule(),
65            var_addr,
66            invoke_context.get_check_aligned(),
67            memory_mapping,
68            invoke_context,
69        )
70    }
71);
72
73declare_builtin_function!(
74    /// Get a EpochRewards sysvar
75    SyscallGetEpochRewardsSysvar,
76    fn rust(
77        invoke_context: &mut InvokeContext,
78        var_addr: u64,
79        _arg2: u64,
80        _arg3: u64,
81        _arg4: u64,
82        _arg5: u64,
83        memory_mapping: &mut MemoryMapping,
84    ) -> Result<u64, Error> {
85        get_sysvar(
86            invoke_context.get_sysvar_cache().get_epoch_rewards(),
87            var_addr,
88            invoke_context.get_check_aligned(),
89            memory_mapping,
90            invoke_context,
91        )
92    }
93);
94
95declare_builtin_function!(
96    /// Get a Fees sysvar
97    SyscallGetFeesSysvar,
98    fn rust(
99        invoke_context: &mut InvokeContext,
100        var_addr: u64,
101        _arg2: u64,
102        _arg3: u64,
103        _arg4: u64,
104        _arg5: u64,
105        memory_mapping: &mut MemoryMapping,
106    ) -> Result<u64, Error> {
107        #[allow(deprecated)]
108        {
109            get_sysvar(
110                invoke_context.get_sysvar_cache().get_fees(),
111                var_addr,
112                invoke_context.get_check_aligned(),
113                memory_mapping,
114                invoke_context,
115            )
116        }
117    }
118);
119
120declare_builtin_function!(
121    /// Get a Rent sysvar
122    SyscallGetRentSysvar,
123    fn rust(
124        invoke_context: &mut InvokeContext,
125        var_addr: u64,
126        _arg2: u64,
127        _arg3: u64,
128        _arg4: u64,
129        _arg5: u64,
130        memory_mapping: &mut MemoryMapping,
131    ) -> Result<u64, Error> {
132        get_sysvar(
133            invoke_context.get_sysvar_cache().get_rent(),
134            var_addr,
135            invoke_context.get_check_aligned(),
136            memory_mapping,
137            invoke_context,
138        )
139    }
140);
141
142declare_builtin_function!(
143    /// Get a Last Restart Slot sysvar
144    SyscallGetLastRestartSlotSysvar,
145    fn rust(
146        invoke_context: &mut InvokeContext,
147        var_addr: u64,
148        _arg2: u64,
149        _arg3: u64,
150        _arg4: u64,
151        _arg5: u64,
152        memory_mapping: &mut MemoryMapping,
153    ) -> Result<u64, Error> {
154        get_sysvar(
155            invoke_context.get_sysvar_cache().get_last_restart_slot(),
156            var_addr,
157            invoke_context.get_check_aligned(),
158            memory_mapping,
159            invoke_context,
160        )
161    }
162);
163
164const SYSVAR_NOT_FOUND: u64 = 2;
165const OFFSET_LENGTH_EXCEEDS_SYSVAR: u64 = 1;
166
167// quoted language from SIMD0127
168// because this syscall can both return error codes and abort, well-ordered error checking is crucial
169declare_builtin_function!(
170    /// Get a slice of a Sysvar in-memory representation
171    SyscallGetSysvar,
172    fn rust(
173        invoke_context: &mut InvokeContext,
174        sysvar_id_addr: u64,
175        var_addr: u64,
176        offset: u64,
177        length: u64,
178        _arg5: u64,
179        memory_mapping: &mut MemoryMapping,
180    ) -> Result<u64, Error> {
181        let check_aligned = invoke_context.get_check_aligned();
182        let ComputeBudget {
183            sysvar_base_cost,
184            cpi_bytes_per_unit,
185            mem_op_base_cost,
186            ..
187        } = *invoke_context.get_compute_budget();
188
189        // Abort: "Compute budget is exceeded."
190        let sysvar_id_cost = 32_u64.checked_div(cpi_bytes_per_unit).unwrap_or(0);
191        let sysvar_buf_cost = length.checked_div(cpi_bytes_per_unit).unwrap_or(0);
192        consume_compute_meter(
193            invoke_context,
194            sysvar_base_cost
195                .saturating_add(sysvar_id_cost)
196                .saturating_add(std::cmp::max(sysvar_buf_cost, mem_op_base_cost)),
197        )?;
198
199        // Abort: "Not all bytes in VM memory range `[sysvar_id, sysvar_id + 32)` are readable."
200        let sysvar_id = translate_type::<Pubkey>(memory_mapping, sysvar_id_addr, check_aligned)?;
201
202        // Abort: "Not all bytes in VM memory range `[var_addr, var_addr + length)` are writable."
203        let var = translate_slice_mut::<u8>(memory_mapping, var_addr, length, check_aligned)?;
204
205        // Abort: "`offset + length` is not in `[0, 2^64)`."
206        let offset_length = offset
207            .checked_add(length)
208            .ok_or(InstructionError::ArithmeticOverflow)?;
209
210        // Abort: "`var_addr + length` is not in `[0, 2^64)`."
211        let _ = var_addr
212            .checked_add(length)
213            .ok_or(InstructionError::ArithmeticOverflow)?;
214
215        let cache = invoke_context.get_sysvar_cache();
216
217        // "`2` if the sysvar data is not present in the Sysvar Cache."
218        let sysvar_buf = match cache.sysvar_id_to_buffer(sysvar_id) {
219            None => return Ok(SYSVAR_NOT_FOUND),
220            Some(ref sysvar_buf) => sysvar_buf,
221        };
222
223        // "`1` if `offset + length` is greater than the length of the sysvar data."
224        if let Some(sysvar_slice) = sysvar_buf.get(offset as usize..offset_length as usize) {
225            var.copy_from_slice(sysvar_slice);
226        } else {
227            return Ok(OFFSET_LENGTH_EXCEEDS_SYSVAR);
228        }
229
230        Ok(SUCCESS)
231    }
232);