Skip to main content

solana_program/
program.rs

1//! Wrappers around [`solana-cpi`] with support for overwriting
2//! syscall stubs
3//!
4//! Solana programs may call other programs, termed [_cross-program
5//! invocations_][cpi] (CPI), with the [`invoke`] and [`invoke_signed`]
6//! functions.
7//!
8//! [`solana-cpi`]: https://docs.rs/solana-cpi/latest/solana_cpi/
9//! [`invoke`]: invoke
10//! [`invoke_signed`]: invoke_signed
11//! [cpi]: https://solana.com/docs/core/cpi
12
13use crate::{
14    account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction, pubkey::Pubkey,
15    stable_layout::stable_instruction::StableInstruction,
16};
17pub use solana_cpi::MAX_RETURN_DATA;
18
19/// Like [`solana_cpi::invoke`], but with support
20/// for overwriting the `sol_invoke_signed` syscall stub.
21///
22/// [`solana_cpi::invoke`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
23pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
24    invoke_signed(instruction, account_infos, &[])
25}
26
27/// Like [`solana_cpi::invoke_unchecked`], but with support
28/// for overwriting the `sol_invoke_signed` syscall stub.
29///
30/// [`solana_cpi::invoke_unchecked`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_unchecked.html
31///
32/// # Safety
33///
34/// __This function is incorrectly missing an `unsafe` declaration.__
35///
36/// If any of the writable accounts passed to the callee contain data that is
37/// borrowed within the calling program, and that data is written to by the
38/// callee, then Rust's aliasing rules will be violated and cause undefined
39/// behavior.
40pub fn invoke_unchecked(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
41    invoke_signed_unchecked(instruction, account_infos, &[])
42}
43
44/// Like [`solana_cpi::invoke_signed`], but with support
45/// for overwriting the `sol_invoke_signed` syscall stub.
46///
47/// [`solana_cpi::invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html
48pub fn invoke_signed(
49    instruction: &Instruction,
50    account_infos: &[AccountInfo],
51    signers_seeds: &[&[&[u8]]],
52) -> ProgramResult {
53    // Check that the account RefCells are consistent with the request
54    for account_meta in instruction.accounts.iter() {
55        for account_info in account_infos.iter() {
56            if account_meta.pubkey == *account_info.key {
57                if account_meta.is_writable {
58                    let _ = account_info.try_borrow_mut_lamports()?;
59                    let _ = account_info.try_borrow_mut_data()?;
60                } else {
61                    let _ = account_info.try_borrow_lamports()?;
62                    let _ = account_info.try_borrow_data()?;
63                }
64                break;
65            }
66        }
67    }
68
69    invoke_signed_unchecked(instruction, account_infos, signers_seeds)
70}
71
72/// Like [`solana_cpi::invoke_signed_unchecked`], but with support
73/// for overwriting the `sol_invoke_signed` syscall stub.
74///
75/// [`solana_cpi::invoke_signed_unchecked`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed_unchecked.html
76///
77/// # Safety
78///
79/// __This function is incorrectly missing an `unsafe` declaration.__
80///
81/// If any of the writable accounts passed to the callee contain data that is
82/// borrowed within the calling program, and that data is written to by the
83/// callee, then Rust's aliasing rules will be violated and cause undefined
84/// behavior.
85pub fn invoke_signed_unchecked(
86    instruction: &Instruction,
87    account_infos: &[AccountInfo],
88    signers_seeds: &[&[&[u8]]],
89) -> ProgramResult {
90    #[cfg(target_os = "solana")]
91    {
92        solana_cpi::invoke_signed_unchecked(instruction, account_infos, signers_seeds)
93    }
94
95    #[cfg(not(target_os = "solana"))]
96    crate::program_stubs::sol_invoke_signed(instruction, account_infos, signers_seeds)
97}
98
99/// Like [`solana_cpi::set_return_data`], but with support
100/// for overwriting the `sol_set_return_data` syscall stub.
101///
102/// [`solana_cpi::set_return_data`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.set_return_data.html
103pub fn set_return_data(data: &[u8]) {
104    #[cfg(target_os = "solana")]
105    {
106        solana_cpi::set_return_data(data);
107    }
108
109    #[cfg(not(target_os = "solana"))]
110    crate::program_stubs::sol_set_return_data(data)
111}
112
113/// Like [`solana_cpi::get_return_data`], but with support
114/// for overwriting the `sol_get_return_data` syscall stub.
115///
116/// [`solana_cpi::get_return_data`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.get_return_data.html
117pub fn get_return_data() -> Option<(Pubkey, Vec<u8>)> {
118    #[cfg(target_os = "solana")]
119    {
120        solana_cpi::get_return_data()
121    }
122
123    #[cfg(not(target_os = "solana"))]
124    crate::program_stubs::sol_get_return_data()
125}
126
127/// Do sanity checks of type layout.
128#[doc(hidden)]
129#[allow(clippy::arithmetic_side_effects)]
130pub fn check_type_assumptions() {
131    use {
132        crate::instruction::AccountMeta,
133        core::mem::offset_of,
134        std::{
135            mem::{align_of, size_of},
136            str::FromStr,
137        },
138    };
139
140    // Code in this file assumes that u64 and usize are the same
141    assert_eq!(size_of::<u64>(), size_of::<usize>());
142    // Code in this file assumes that u8 is byte aligned
143    assert_eq!(1, align_of::<u8>());
144
145    // Enforce Instruction layout
146    {
147        assert_eq!(size_of::<AccountMeta>(), 32 + 1 + 1);
148
149        let pubkey1 = Pubkey::from_str("J9PYCcoKusHyKRMXnBL17VTXC3MVETyqBG2KyLXVv6Ai").unwrap();
150        let pubkey2 = Pubkey::from_str("Hvy4GHgPToZNoENTKjC4mJqpzWWjgTwXrFufKfxYiKkV").unwrap();
151        let pubkey3 = Pubkey::from_str("JDMyRL8rCkae7maCSv47upNuBMFd3Mgos1fz2AvYzVzY").unwrap();
152        let account_meta1 = AccountMeta {
153            pubkey: pubkey2,
154            is_signer: true,
155            is_writable: false,
156        };
157        let account_meta2 = AccountMeta {
158            pubkey: pubkey3,
159            is_signer: false,
160            is_writable: true,
161        };
162        let data = vec![1, 2, 3, 4, 5];
163        let instruction = Instruction {
164            program_id: pubkey1,
165            accounts: vec![account_meta1.clone(), account_meta2.clone()],
166            data: data.clone(),
167        };
168        let instruction = StableInstruction::from(instruction);
169        let instruction_addr = &instruction as *const _ as u64;
170
171        // program id
172        assert_eq!(offset_of!(StableInstruction, program_id), 48);
173        let pubkey_ptr = (instruction_addr + 48) as *const Pubkey;
174        unsafe {
175            assert_eq!(*pubkey_ptr, pubkey1);
176        }
177
178        // accounts
179        assert_eq!(offset_of!(StableInstruction, accounts), 0);
180        let accounts_ptr = (instruction_addr) as *const *const AccountMeta;
181        let accounts_cap = (instruction_addr + 8) as *const usize;
182        let accounts_len = (instruction_addr + 16) as *const usize;
183        unsafe {
184            assert_eq!(*accounts_cap, 2);
185            assert_eq!(*accounts_len, 2);
186            let account_meta_ptr = *accounts_ptr;
187            assert_eq!(*account_meta_ptr, account_meta1);
188            assert_eq!(*(account_meta_ptr.offset(1)), account_meta2);
189        }
190
191        // data
192        assert_eq!(offset_of!(StableInstruction, data), 24);
193        let data_ptr = (instruction_addr + 24) as *const *const [u8; 5];
194        let data_cap = (instruction_addr + 24 + 8) as *const usize;
195        let data_len = (instruction_addr + 24 + 16) as *const usize;
196        unsafe {
197            assert_eq!(*data_cap, 5);
198
199            assert_eq!(*data_len, 5);
200            let u8_ptr = *data_ptr;
201            assert_eq!(*u8_ptr, data[..]);
202        }
203    }
204
205    solana_account_info::check_type_assumptions();
206}
207
208#[cfg(test)]
209mod tests {
210    #[test]
211    fn test_check_type_assumptions() {
212        super::check_type_assumptions()
213    }
214}