solana_bpf_loader_program/syscalls/
sysvar.rs

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