miraland_program/
program_stubs.rs

1//! Implementations of syscalls used when `miraland-program` is built for non-SBF targets.
2
3#![cfg(not(target_os = "solana"))]
4
5use {
6    crate::{
7        account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
8        program_error::UNSUPPORTED_SYSVAR, pubkey::Pubkey,
9    },
10    base64::{prelude::BASE64_STANDARD, Engine},
11    itertools::Itertools,
12    std::sync::{Arc, RwLock},
13};
14
15lazy_static::lazy_static! {
16    static ref SYSCALL_STUBS: Arc<RwLock<Box<dyn SyscallStubs>>> = Arc::new(RwLock::new(Box::new(DefaultSyscallStubs {})));
17}
18
19// The default syscall stubs may not do much, but `set_syscalls()` can be used
20// to swap in alternatives
21pub fn set_syscall_stubs(syscall_stubs: Box<dyn SyscallStubs>) -> Box<dyn SyscallStubs> {
22    std::mem::replace(&mut SYSCALL_STUBS.write().unwrap(), syscall_stubs)
23}
24
25#[allow(clippy::arithmetic_side_effects)]
26pub trait SyscallStubs: Sync + Send {
27    fn sol_log(&self, message: &str) {
28        println!("{message}");
29    }
30    fn sol_log_compute_units(&self) {
31        sol_log("SyscallStubs: sol_log_compute_units() not available");
32    }
33    fn sol_remaining_compute_units(&self) -> u64 {
34        sol_log("SyscallStubs: sol_remaining_compute_units() defaulting to 0");
35        0
36    }
37    fn sol_invoke_signed(
38        &self,
39        _instruction: &Instruction,
40        _account_infos: &[AccountInfo],
41        _signers_seeds: &[&[&[u8]]],
42    ) -> ProgramResult {
43        sol_log("SyscallStubs: sol_invoke_signed() not available");
44        Ok(())
45    }
46    fn sol_get_clock_sysvar(&self, _var_addr: *mut u8) -> u64 {
47        UNSUPPORTED_SYSVAR
48    }
49    fn sol_get_epoch_schedule_sysvar(&self, _var_addr: *mut u8) -> u64 {
50        UNSUPPORTED_SYSVAR
51    }
52    fn sol_get_fees_sysvar(&self, _var_addr: *mut u8) -> u64 {
53        UNSUPPORTED_SYSVAR
54    }
55    fn sol_get_rent_sysvar(&self, _var_addr: *mut u8) -> u64 {
56        UNSUPPORTED_SYSVAR
57    }
58    fn sol_get_epoch_rewards_sysvar(&self, _var_addr: *mut u8) -> u64 {
59        UNSUPPORTED_SYSVAR
60    }
61    fn sol_get_last_restart_slot(&self, _var_addr: *mut u8) -> u64 {
62        UNSUPPORTED_SYSVAR
63    }
64    /// # Safety
65    unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) {
66        // cannot be overlapping
67        assert!(
68            is_nonoverlapping(src as usize, n, dst as usize, n),
69            "memcpy does not support overlapping regions"
70        );
71        std::ptr::copy_nonoverlapping(src, dst, n);
72    }
73    /// # Safety
74    unsafe fn sol_memmove(&self, dst: *mut u8, src: *const u8, n: usize) {
75        std::ptr::copy(src, dst, n);
76    }
77    /// # Safety
78    unsafe fn sol_memcmp(&self, s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
79        let mut i = 0;
80        while i < n {
81            let a = *s1.add(i);
82            let b = *s2.add(i);
83            if a != b {
84                *result = a as i32 - b as i32;
85                return;
86            }
87            i += 1;
88        }
89        *result = 0
90    }
91    /// # Safety
92    unsafe fn sol_memset(&self, s: *mut u8, c: u8, n: usize) {
93        let s = std::slice::from_raw_parts_mut(s, n);
94        for val in s.iter_mut().take(n) {
95            *val = c;
96        }
97    }
98    fn sol_get_return_data(&self) -> Option<(Pubkey, Vec<u8>)> {
99        None
100    }
101    fn sol_set_return_data(&self, _data: &[u8]) {}
102    fn sol_log_data(&self, fields: &[&[u8]]) {
103        println!(
104            "data: {}",
105            fields.iter().map(|v| BASE64_STANDARD.encode(v)).join(" ")
106        );
107    }
108    fn sol_get_processed_sibling_instruction(&self, _index: usize) -> Option<Instruction> {
109        None
110    }
111    fn sol_get_stack_height(&self) -> u64 {
112        0
113    }
114}
115
116struct DefaultSyscallStubs {}
117impl SyscallStubs for DefaultSyscallStubs {}
118
119pub(crate) fn sol_log(message: &str) {
120    SYSCALL_STUBS.read().unwrap().sol_log(message);
121}
122
123pub(crate) fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) {
124    sol_log(&format!(
125        "{arg1:#x}, {arg2:#x}, {arg3:#x}, {arg4:#x}, {arg5:#x}"
126    ));
127}
128
129pub(crate) fn sol_log_compute_units() {
130    SYSCALL_STUBS.read().unwrap().sol_log_compute_units();
131}
132
133pub(crate) fn sol_remaining_compute_units() -> u64 {
134    SYSCALL_STUBS.read().unwrap().sol_remaining_compute_units()
135}
136
137pub(crate) fn sol_invoke_signed(
138    instruction: &Instruction,
139    account_infos: &[AccountInfo],
140    signers_seeds: &[&[&[u8]]],
141) -> ProgramResult {
142    SYSCALL_STUBS
143        .read()
144        .unwrap()
145        .sol_invoke_signed(instruction, account_infos, signers_seeds)
146}
147
148pub(crate) fn sol_get_clock_sysvar(var_addr: *mut u8) -> u64 {
149    SYSCALL_STUBS.read().unwrap().sol_get_clock_sysvar(var_addr)
150}
151
152pub(crate) fn sol_get_epoch_schedule_sysvar(var_addr: *mut u8) -> u64 {
153    SYSCALL_STUBS
154        .read()
155        .unwrap()
156        .sol_get_epoch_schedule_sysvar(var_addr)
157}
158
159pub(crate) fn sol_get_fees_sysvar(var_addr: *mut u8) -> u64 {
160    SYSCALL_STUBS.read().unwrap().sol_get_fees_sysvar(var_addr)
161}
162
163pub(crate) fn sol_get_rent_sysvar(var_addr: *mut u8) -> u64 {
164    SYSCALL_STUBS.read().unwrap().sol_get_rent_sysvar(var_addr)
165}
166
167pub(crate) fn sol_get_last_restart_slot(var_addr: *mut u8) -> u64 {
168    SYSCALL_STUBS
169        .read()
170        .unwrap()
171        .sol_get_last_restart_slot(var_addr)
172}
173
174pub(crate) fn sol_memcpy(dst: *mut u8, src: *const u8, n: usize) {
175    unsafe {
176        SYSCALL_STUBS.read().unwrap().sol_memcpy(dst, src, n);
177    }
178}
179
180pub(crate) fn sol_memmove(dst: *mut u8, src: *const u8, n: usize) {
181    unsafe {
182        SYSCALL_STUBS.read().unwrap().sol_memmove(dst, src, n);
183    }
184}
185
186pub(crate) fn sol_memcmp(s1: *const u8, s2: *const u8, n: usize, result: *mut i32) {
187    unsafe {
188        SYSCALL_STUBS.read().unwrap().sol_memcmp(s1, s2, n, result);
189    }
190}
191
192pub(crate) fn sol_memset(s: *mut u8, c: u8, n: usize) {
193    unsafe {
194        SYSCALL_STUBS.read().unwrap().sol_memset(s, c, n);
195    }
196}
197
198pub(crate) fn sol_get_return_data() -> Option<(Pubkey, Vec<u8>)> {
199    SYSCALL_STUBS.read().unwrap().sol_get_return_data()
200}
201
202pub(crate) fn sol_set_return_data(data: &[u8]) {
203    SYSCALL_STUBS.read().unwrap().sol_set_return_data(data)
204}
205
206pub(crate) fn sol_log_data(data: &[&[u8]]) {
207    SYSCALL_STUBS.read().unwrap().sol_log_data(data)
208}
209
210pub(crate) fn sol_get_processed_sibling_instruction(index: usize) -> Option<Instruction> {
211    SYSCALL_STUBS
212        .read()
213        .unwrap()
214        .sol_get_processed_sibling_instruction(index)
215}
216
217pub(crate) fn sol_get_stack_height() -> u64 {
218    SYSCALL_STUBS.read().unwrap().sol_get_stack_height()
219}
220
221pub(crate) fn sol_get_epoch_rewards_sysvar(var_addr: *mut u8) -> u64 {
222    SYSCALL_STUBS
223        .read()
224        .unwrap()
225        .sol_get_epoch_rewards_sysvar(var_addr)
226}
227
228/// Check that two regions do not overlap.
229///
230/// Hidden to share with bpf_loader without being part of the API surface.
231#[doc(hidden)]
232pub fn is_nonoverlapping<N>(src: N, src_len: N, dst: N, dst_len: N) -> bool
233where
234    N: Ord + num_traits::SaturatingSub,
235{
236    // If the absolute distance between the ptrs is at least as big as the size of the other,
237    // they do not overlap.
238    if src > dst {
239        src.saturating_sub(&dst) >= dst_len
240    } else {
241        dst.saturating_sub(&src) >= src_len
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248
249    #[test]
250    fn test_is_nonoverlapping() {
251        for dst in 0..8 {
252            assert!(is_nonoverlapping(10, 3, dst, 3));
253        }
254        for dst in 8..13 {
255            assert!(!is_nonoverlapping(10, 3, dst, 3));
256        }
257        for dst in 13..20 {
258            assert!(is_nonoverlapping(10, 3, dst, 3));
259        }
260        assert!(is_nonoverlapping::<u8>(255, 3, 254, 1));
261        assert!(!is_nonoverlapping::<u8>(255, 2, 254, 3));
262    }
263}