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    extern crate memoffset;
132    use {
133        crate::instruction::AccountMeta,
134        memoffset::offset_of,
135        std::{
136            mem::{align_of, size_of},
137            str::FromStr,
138        },
139    };
140
141    // Code in this file assumes that u64 and usize are the same
142    assert_eq!(size_of::<u64>(), size_of::<usize>());
143    // Code in this file assumes that u8 is byte aligned
144    assert_eq!(1, align_of::<u8>());
145
146    // Enforce Instruction layout
147    {
148        assert_eq!(size_of::<AccountMeta>(), 32 + 1 + 1);
149
150        let pubkey1 = Pubkey::from_str("J9PYCcoKusHyKRMXnBL17VTXC3MVETyqBG2KyLXVv6Ai").unwrap();
151        let pubkey2 = Pubkey::from_str("Hvy4GHgPToZNoENTKjC4mJqpzWWjgTwXrFufKfxYiKkV").unwrap();
152        let pubkey3 = Pubkey::from_str("JDMyRL8rCkae7maCSv47upNuBMFd3Mgos1fz2AvYzVzY").unwrap();
153        let account_meta1 = AccountMeta {
154            pubkey: pubkey2,
155            is_signer: true,
156            is_writable: false,
157        };
158        let account_meta2 = AccountMeta {
159            pubkey: pubkey3,
160            is_signer: false,
161            is_writable: true,
162        };
163        let data = vec![1, 2, 3, 4, 5];
164        let instruction = Instruction {
165            program_id: pubkey1,
166            accounts: vec![account_meta1.clone(), account_meta2.clone()],
167            data: data.clone(),
168        };
169        let instruction = StableInstruction::from(instruction);
170        let instruction_addr = &instruction as *const _ as u64;
171
172        // program id
173        assert_eq!(offset_of!(StableInstruction, program_id), 48);
174        let pubkey_ptr = (instruction_addr + 48) as *const Pubkey;
175        unsafe {
176            assert_eq!(*pubkey_ptr, pubkey1);
177        }
178
179        // accounts
180        assert_eq!(offset_of!(StableInstruction, accounts), 0);
181        let accounts_ptr = (instruction_addr) as *const *const AccountMeta;
182        let accounts_cap = (instruction_addr + 8) as *const usize;
183        let accounts_len = (instruction_addr + 16) as *const usize;
184        unsafe {
185            assert_eq!(*accounts_cap, 2);
186            assert_eq!(*accounts_len, 2);
187            let account_meta_ptr = *accounts_ptr;
188            assert_eq!(*account_meta_ptr, account_meta1);
189            assert_eq!(*(account_meta_ptr.offset(1)), account_meta2);
190        }
191
192        // data
193        assert_eq!(offset_of!(StableInstruction, data), 24);
194        let data_ptr = (instruction_addr + 24) as *const *const [u8; 5];
195        let data_cap = (instruction_addr + 24 + 8) as *const usize;
196        let data_len = (instruction_addr + 24 + 16) as *const usize;
197        unsafe {
198            assert_eq!(*data_cap, 5);
199
200            assert_eq!(*data_len, 5);
201            let u8_ptr = *data_ptr;
202            assert_eq!(*u8_ptr, data[..]);
203        }
204    }
205
206    solana_account_info::check_type_assumptions();
207}
208
209#[cfg(test)]
210mod tests {
211    #[test]
212    fn test_check_type_assumptions() {
213        super::check_type_assumptions()
214    }
215}