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
13pub use solana_cpi::MAX_RETURN_DATA;
14use {
15    crate::{
16        account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
17        pubkey::Pubkey, stable_layout::stable_instruction::StableInstruction,
18    },
19    solana_clock::Epoch,
20};
21
22/// Like [`solana_cpi::invoke`], but with support
23/// for overwriting the `sol_invoke_signed` syscall stub.
24///
25/// [`solana_cpi::invoke`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
26pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
27    invoke_signed(instruction, account_infos, &[])
28}
29
30/// Like [`solana_cpi::invoke_unchecked`], but with support
31/// for overwriting the `sol_invoke_signed` syscall stub.
32///
33/// [`solana_cpi::invoke_unchecked`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_unchecked.html
34///
35/// # Safety
36///
37/// __This function is incorrectly missing an `unsafe` declaration.__
38///
39/// If any of the writable accounts passed to the callee contain data that is
40/// borrowed within the calling program, and that data is written to by the
41/// callee, then Rust's aliasing rules will be violated and cause undefined
42/// behavior.
43pub fn invoke_unchecked(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
44    invoke_signed_unchecked(instruction, account_infos, &[])
45}
46
47/// Like [`solana_cpi::invoke_signed`], but with support
48/// for overwriting the `sol_invoke_signed` syscall stub.
49///
50/// [`solana_cpi::invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html
51pub fn invoke_signed(
52    instruction: &Instruction,
53    account_infos: &[AccountInfo],
54    signers_seeds: &[&[&[u8]]],
55) -> ProgramResult {
56    // Check that the account RefCells are consistent with the request
57    for account_meta in instruction.accounts.iter() {
58        for account_info in account_infos.iter() {
59            if account_meta.pubkey == *account_info.key {
60                if account_meta.is_writable {
61                    let _ = account_info.try_borrow_mut_lamports()?;
62                    let _ = account_info.try_borrow_mut_data()?;
63                } else {
64                    let _ = account_info.try_borrow_lamports()?;
65                    let _ = account_info.try_borrow_data()?;
66                }
67                break;
68            }
69        }
70    }
71
72    invoke_signed_unchecked(instruction, account_infos, signers_seeds)
73}
74
75/// Like [`solana_cpi::invoke_signed_unchecked`], but with support
76/// for overwriting the `sol_invoke_signed` syscall stub.
77///
78/// [`solana_cpi::invoke_signed_unchecked`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed_unchecked.html
79///
80/// # Safety
81///
82/// __This function is incorrectly missing an `unsafe` declaration.__
83///
84/// If any of the writable accounts passed to the callee contain data that is
85/// borrowed within the calling program, and that data is written to by the
86/// callee, then Rust's aliasing rules will be violated and cause undefined
87/// behavior.
88pub fn invoke_signed_unchecked(
89    instruction: &Instruction,
90    account_infos: &[AccountInfo],
91    signers_seeds: &[&[&[u8]]],
92) -> ProgramResult {
93    #[cfg(target_os = "solana")]
94    {
95        solana_cpi::invoke_signed_unchecked(instruction, account_infos, signers_seeds)
96    }
97
98    #[cfg(not(target_os = "solana"))]
99    crate::program_stubs::sol_invoke_signed(instruction, account_infos, signers_seeds)
100}
101
102/// Like [`solana_cpi::set_return_data`], but with support
103/// for overwriting the `sol_set_return_data` syscall stub.
104///
105/// [`solana_cpi::set_return_data`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.set_return_data.html
106pub fn set_return_data(data: &[u8]) {
107    #[cfg(target_os = "solana")]
108    {
109        solana_cpi::set_return_data(data);
110    }
111
112    #[cfg(not(target_os = "solana"))]
113    crate::program_stubs::sol_set_return_data(data)
114}
115
116/// Like [`solana_cpi::get_return_data`], but with support
117/// for overwriting the `sol_get_return_data` syscall stub.
118///
119/// [`solana_cpi::get_return_data`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.get_return_data.html
120pub fn get_return_data() -> Option<(Pubkey, Vec<u8>)> {
121    #[cfg(target_os = "solana")]
122    {
123        solana_cpi::get_return_data()
124    }
125
126    #[cfg(not(target_os = "solana"))]
127    crate::program_stubs::sol_get_return_data()
128}
129
130/// Do sanity checks of type layout.
131#[doc(hidden)]
132#[allow(clippy::arithmetic_side_effects)]
133pub fn check_type_assumptions() {
134    extern crate memoffset;
135    use {
136        crate::instruction::AccountMeta,
137        memoffset::offset_of,
138        std::{
139            cell::RefCell,
140            mem::{align_of, size_of},
141            rc::Rc,
142            str::FromStr,
143        },
144    };
145
146    // Code in this file assumes that u64 and usize are the same
147    assert_eq!(size_of::<u64>(), size_of::<usize>());
148    // Code in this file assumes that u8 is byte aligned
149    assert_eq!(1, align_of::<u8>());
150
151    // Enforce Instruction layout
152    {
153        assert_eq!(size_of::<AccountMeta>(), 32 + 1 + 1);
154
155        let pubkey1 = Pubkey::from_str("J9PYCcoKusHyKRMXnBL17VTXC3MVETyqBG2KyLXVv6Ai").unwrap();
156        let pubkey2 = Pubkey::from_str("Hvy4GHgPToZNoENTKjC4mJqpzWWjgTwXrFufKfxYiKkV").unwrap();
157        let pubkey3 = Pubkey::from_str("JDMyRL8rCkae7maCSv47upNuBMFd3Mgos1fz2AvYzVzY").unwrap();
158        let account_meta1 = AccountMeta {
159            pubkey: pubkey2,
160            is_signer: true,
161            is_writable: false,
162        };
163        let account_meta2 = AccountMeta {
164            pubkey: pubkey3,
165            is_signer: false,
166            is_writable: true,
167        };
168        let data = vec![1, 2, 3, 4, 5];
169        let instruction = Instruction {
170            program_id: pubkey1,
171            accounts: vec![account_meta1.clone(), account_meta2.clone()],
172            data: data.clone(),
173        };
174        let instruction = StableInstruction::from(instruction);
175        let instruction_addr = &instruction as *const _ as u64;
176
177        // program id
178        assert_eq!(offset_of!(StableInstruction, program_id), 48);
179        let pubkey_ptr = (instruction_addr + 48) as *const Pubkey;
180        unsafe {
181            assert_eq!(*pubkey_ptr, pubkey1);
182        }
183
184        // accounts
185        assert_eq!(offset_of!(StableInstruction, accounts), 0);
186        let accounts_ptr = (instruction_addr) as *const *const AccountMeta;
187        let accounts_cap = (instruction_addr + 8) as *const usize;
188        let accounts_len = (instruction_addr + 16) as *const usize;
189        unsafe {
190            assert_eq!(*accounts_cap, 2);
191            assert_eq!(*accounts_len, 2);
192            let account_meta_ptr = *accounts_ptr;
193            assert_eq!(*account_meta_ptr, account_meta1);
194            assert_eq!(*(account_meta_ptr.offset(1)), account_meta2);
195        }
196
197        // data
198        assert_eq!(offset_of!(StableInstruction, data), 24);
199        let data_ptr = (instruction_addr + 24) as *const *const [u8; 5];
200        let data_cap = (instruction_addr + 24 + 8) as *const usize;
201        let data_len = (instruction_addr + 24 + 16) as *const usize;
202        unsafe {
203            assert_eq!(*data_cap, 5);
204
205            assert_eq!(*data_len, 5);
206            let u8_ptr = *data_ptr;
207            assert_eq!(*u8_ptr, data[..]);
208        }
209    }
210
211    // Enforce AccountInfo layout
212    {
213        let key = Pubkey::from_str("6o8R9NsUxNskF1MfWM1f265y4w86JYbEwqCmTacdLkHp").unwrap();
214        let mut lamports = 31;
215        let mut data = vec![1, 2, 3, 4, 5];
216        let owner = Pubkey::from_str("2tjK4XyNU54XdN9jokx46QzLybbLVGwQQvTfhcuBXAjR").unwrap();
217        let account_info = AccountInfo {
218            key: &key,
219            is_signer: true,
220            is_writable: false,
221            lamports: Rc::new(RefCell::new(&mut lamports)),
222            data: Rc::new(RefCell::new(&mut data)),
223            owner: &owner,
224            executable: true,
225            rent_epoch: 42,
226        };
227        let account_info_addr = &account_info as *const _ as u64;
228
229        // key
230        assert_eq!(offset_of!(AccountInfo, key), 0);
231        let key_ptr = (account_info_addr) as *const &Pubkey;
232        unsafe {
233            assert_eq!(**key_ptr, key);
234        }
235
236        // lamports
237        assert_eq!(offset_of!(AccountInfo, lamports), 8);
238        let lamports_ptr = (account_info_addr + 8) as *const Rc<RefCell<&mut u64>>;
239        unsafe {
240            assert_eq!(**(*lamports_ptr).as_ptr(), 31);
241        }
242
243        // data
244        assert_eq!(offset_of!(AccountInfo, data), 16);
245        let data_ptr = (account_info_addr + 16) as *const Rc<RefCell<&mut [u8]>>;
246        unsafe {
247            assert_eq!((*(*data_ptr).as_ptr())[..], data[..]);
248        }
249
250        // owner
251        assert_eq!(offset_of!(AccountInfo, owner), 24);
252        let owner_ptr = (account_info_addr + 24) as *const &Pubkey;
253        unsafe {
254            assert_eq!(**owner_ptr, owner);
255        }
256
257        // rent_epoch
258        assert_eq!(offset_of!(AccountInfo, rent_epoch), 32);
259        let renbt_epoch_ptr = (account_info_addr + 32) as *const Epoch;
260        unsafe {
261            assert_eq!(*renbt_epoch_ptr, 42);
262        }
263
264        // is_signer
265        assert_eq!(offset_of!(AccountInfo, is_signer), 40);
266        let is_signer_ptr = (account_info_addr + 40) as *const bool;
267        unsafe {
268            assert!(*is_signer_ptr);
269        }
270
271        // is_writable
272        assert_eq!(offset_of!(AccountInfo, is_writable), 41);
273        let is_writable_ptr = (account_info_addr + 41) as *const bool;
274        unsafe {
275            assert!(!*is_writable_ptr);
276        }
277
278        // executable
279        assert_eq!(offset_of!(AccountInfo, executable), 42);
280        let executable_ptr = (account_info_addr + 42) as *const bool;
281        unsafe {
282            assert!(*executable_ptr);
283        }
284    }
285}
286
287#[cfg(test)]
288mod tests {
289    #[test]
290    fn test_check_type_assumptions() {
291        super::check_type_assumptions()
292    }
293}