Skip to main content

rialo_s_bpf_loader_program/syscalls/
sysvar.rs

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