Skip to main content

sbpf_runtime/syscalls/
sysvar.rs

1use {
2    crate::config::{ExecutionCost, SysvarContext},
3    sbpf_vm::{compute::ComputeMeter, errors::SbpfVmResult, memory::Memory},
4    solana_clock::Clock,
5    solana_epoch_schedule::EpochSchedule,
6    solana_last_restart_slot::LastRestartSlot,
7    solana_rent::Rent,
8    std::mem::size_of,
9};
10
11fn write_sysvar_bytes<T>(memory: &mut Memory, addr: u64, sysvar: &T) -> SbpfVmResult<()> {
12    let bytes =
13        unsafe { std::slice::from_raw_parts(sysvar as *const T as *const u8, size_of::<T>()) };
14    memory.write_bytes(addr, bytes)
15}
16
17pub fn sol_get_clock_sysvar(
18    registers: [u64; 5],
19    memory: &mut Memory,
20    compute: &ComputeMeter,
21    costs: &ExecutionCost,
22    sysvars: &SysvarContext,
23) -> SbpfVmResult<u64> {
24    compute.consume(
25        costs
26            .sysvar_base_cost
27            .saturating_add(size_of::<Clock>() as u64),
28    )?;
29    write_sysvar_bytes(memory, registers[0], &sysvars.clock)?;
30    Ok(0)
31}
32
33pub fn sol_get_rent_sysvar(
34    registers: [u64; 5],
35    memory: &mut Memory,
36    compute: &ComputeMeter,
37    costs: &ExecutionCost,
38    sysvars: &SysvarContext,
39) -> SbpfVmResult<u64> {
40    compute.consume(
41        costs
42            .sysvar_base_cost
43            .saturating_add(size_of::<Rent>() as u64),
44    )?;
45    write_sysvar_bytes(memory, registers[0], &sysvars.rent)?;
46    Ok(0)
47}
48
49pub fn sol_get_epoch_schedule_sysvar(
50    registers: [u64; 5],
51    memory: &mut Memory,
52    compute: &ComputeMeter,
53    costs: &ExecutionCost,
54    sysvars: &SysvarContext,
55) -> SbpfVmResult<u64> {
56    compute.consume(
57        costs
58            .sysvar_base_cost
59            .saturating_add(size_of::<EpochSchedule>() as u64),
60    )?;
61    write_sysvar_bytes(memory, registers[0], &sysvars.epoch_schedule)?;
62    Ok(0)
63}
64
65pub fn sol_get_last_restart_slot_sysvar(
66    registers: [u64; 5],
67    memory: &mut Memory,
68    compute: &ComputeMeter,
69    costs: &ExecutionCost,
70    sysvars: &SysvarContext,
71) -> SbpfVmResult<u64> {
72    compute.consume(
73        costs
74            .sysvar_base_cost
75            .saturating_add(size_of::<LastRestartSlot>() as u64),
76    )?;
77    write_sysvar_bytes(memory, registers[0], &sysvars.last_restart_slot)?;
78    Ok(0)
79}
80
81#[cfg(test)]
82mod tests {
83    use {
84        super::*,
85        crate::{
86            config::SysvarContext,
87            syscalls::tests::test_helpers::{costs, make_memory, meter},
88        },
89        sbpf_vm::{errors::SbpfVmError, memory::Memory},
90    };
91
92    fn raw_bytes<T>(val: &T) -> Vec<u8> {
93        unsafe { std::slice::from_raw_parts(val as *const T as *const u8, size_of::<T>()).to_vec() }
94    }
95
96    #[test]
97    fn test_sol_get_clock_sysvar() {
98        let mut memory = make_memory();
99        let mut sysvars = SysvarContext::default();
100        sysvars.clock.slot = 99_999;
101        sysvars.clock.unix_timestamp = 1_700_000_000;
102
103        let addr = Memory::HEAP_START;
104        let registers = [addr, 0, 0, 0, 0];
105        sol_get_clock_sysvar(
106            registers,
107            &mut memory,
108            &meter(1_000_000),
109            &costs(),
110            &sysvars,
111        )
112        .unwrap();
113
114        let written = memory.read_bytes(addr, size_of::<Clock>()).unwrap();
115        assert_eq!(written, raw_bytes(&sysvars.clock).as_slice());
116    }
117
118    #[test]
119    fn test_sol_get_clock_sysvar_compute_exhausted() {
120        let mut memory = make_memory();
121        let sysvars = SysvarContext::default();
122        let cost = costs().sysvar_base_cost + size_of::<Clock>() as u64;
123        let registers = [Memory::HEAP_START, 0, 0, 0, 0];
124        assert!(matches!(
125            sol_get_clock_sysvar(registers, &mut memory, &meter(cost - 1), &costs(), &sysvars),
126            Err(SbpfVmError::ComputeBudgetExceeded { .. })
127        ));
128    }
129
130    #[test]
131    fn test_sol_get_rent_sysvar() {
132        let mut memory = make_memory();
133        let sysvars = SysvarContext {
134            rent: Rent::with_lamports_per_byte(3_480),
135            ..Default::default()
136        };
137
138        let addr = Memory::HEAP_START;
139        let registers = [addr, 0, 0, 0, 0];
140        sol_get_rent_sysvar(
141            registers,
142            &mut memory,
143            &meter(1_000_000),
144            &costs(),
145            &sysvars,
146        )
147        .unwrap();
148
149        let written = memory.read_bytes(addr, size_of::<Rent>()).unwrap();
150        assert_eq!(written, raw_bytes(&sysvars.rent).as_slice());
151    }
152
153    #[test]
154    fn test_sol_get_epoch_schedule_sysvar() {
155        let mut memory = make_memory();
156        let mut sysvars = SysvarContext::default();
157        sysvars.epoch_schedule.slots_per_epoch = 432_000;
158
159        let addr = Memory::HEAP_START;
160        let registers = [addr, 0, 0, 0, 0];
161        sol_get_epoch_schedule_sysvar(
162            registers,
163            &mut memory,
164            &meter(1_000_000),
165            &costs(),
166            &sysvars,
167        )
168        .unwrap();
169
170        let written = memory.read_bytes(addr, size_of::<EpochSchedule>()).unwrap();
171        assert_eq!(written, raw_bytes(&sysvars.epoch_schedule).as_slice());
172    }
173
174    #[test]
175    fn test_sol_get_last_restart_slot_sysvar() {
176        let mut memory = make_memory();
177        let mut sysvars = SysvarContext::default();
178        sysvars.last_restart_slot.last_restart_slot = 12_345_678;
179
180        let addr = Memory::HEAP_START;
181        let registers = [addr, 0, 0, 0, 0];
182        sol_get_last_restart_slot_sysvar(
183            registers,
184            &mut memory,
185            &meter(1_000_000),
186            &costs(),
187            &sysvars,
188        )
189        .unwrap();
190
191        let written = memory
192            .read_bytes(addr, size_of::<LastRestartSlot>())
193            .unwrap();
194        assert_eq!(written, raw_bytes(&sysvars.last_restart_slot).as_slice());
195    }
196}