waffles_solana_program/
program_stubs.rs

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