miraland_program/
program.rs

1//! Cross-program invocation.
2//!
3//! Miraland programs may call other programs, termed [_cross-program
4//! invocations_][cpi] (CPI), with the [`invoke`] and [`invoke_signed`]
5//! functions.
6//!
7//! [`invoke`]: invoke
8//! [`invoke_signed`]: invoke_signed
9//! [cpi]: https://docs.solana.com/developing/programming-model/calling-between-programs
10
11use crate::{
12    account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction, pubkey::Pubkey,
13    stable_layout::stable_instruction::StableInstruction,
14};
15
16/// Invoke a cross-program instruction.
17///
18/// Invoking one program from another program requires an [`Instruction`]
19/// containing the program ID of the other program, instruction data that
20/// will be understood by the other program, and a list of [`AccountInfo`]s
21/// corresponding to all of the accounts accessed by the other program. Because
22/// the only way for a program to acquire `AccountInfo` values is by receiving
23/// them from the runtime at the [program entrypoint][entrypoint!], any account
24/// required by the callee program must transitively be required by the caller
25/// program, and provided by _its_ caller. The same is true of the program ID of
26/// the called program.
27///
28/// [entrypoint!]: crate::entrypoint!
29///
30/// The `Instruction` is usually built from within the calling program, but may
31/// be deserialized from an external source.
32///
33/// This function will not return if the called program returns anything other
34/// than success. If the callee returns an error or aborts then the entire
35/// transaction will immediately fail. To return data as the result of a
36/// cross-program invocation use the [`set_return_data`] / [`get_return_data`]
37/// functions, or have the callee write to a dedicated account for that purpose.
38///
39/// A program may directly call itself recursively, but may not be indirectly
40/// called recursively (reentered) by another program. Indirect reentrancy will
41/// cause the transaction to immediately fail.
42///
43/// # Validation of shared data between programs
44///
45/// The `AccountInfo` structures passed to this function contain data that is
46/// directly accessed by the runtime and is copied to and from the memory space
47/// of the called program. Some of that data, the [`AccountInfo::lamports`] and
48/// [`AccountInfo::data`] fields, may be mutated as a side-effect of the called
49/// program, if that program has writable access to the given account.
50///
51/// These two fields are stored in [`RefCell`]s to enforce the aliasing
52/// discipline for mutated values required by the Rust language. Prior to
53/// invoking the runtime, this routine will test that each `RefCell` is
54/// borrowable as required by the callee and return an error if not.
55///
56/// The CPU cost of these runtime checks can be avoided with the unsafe
57/// [`invoke_unchecked`] function.
58///
59/// [`RefCell`]: std::cell::RefCell
60///
61/// # Errors
62///
63/// If the called program completes successfully and violates no runtime
64/// invariants, then this function will return successfully. If the callee
65/// completes and returns a [`ProgramError`], then the transaction will
66/// immediately fail. Control will not return to the caller.
67///
68/// Various runtime invariants are checked before the callee is invoked and
69/// before returning control to the caller. If any of these invariants are
70/// violated then the transaction will immediately fail. A non-exhaustive list
71/// of these invariants includes:
72///
73/// - The sum of lamports owned by all referenced accounts has not changed.
74/// - A program has not debited lamports from an account it does not own.
75/// - A program has not otherwise written to an account that it does not own.
76/// - A program has not written to an account that is not writable.
77/// - The size of account data has not exceeded applicable limits.
78///
79/// If the invoked program does not exist or is not executable then
80/// the transaction will immediately fail.
81///
82/// If any of the `RefCell`s within the provided `AccountInfo`s cannot be
83/// borrowed in accordance with the call's requirements, an error of
84/// [`ProgramError::AccountBorrowFailed`] is returned.
85///
86/// [`ProgramError`]: crate::program_error::ProgramError
87/// [`ProgramError::AccountBorrowFailed`]: crate::program_error::ProgramError::AccountBorrowFailed
88///
89/// # Examples
90///
91/// A simple example of transferring lamports via CPI:
92///
93/// ```
94/// use miraland_program::{
95///     account_info::{next_account_info, AccountInfo},
96///     entrypoint,
97///     entrypoint::ProgramResult,
98///     program::invoke,
99///     pubkey::Pubkey,
100///     system_instruction,
101///     system_program,
102/// };
103///
104/// entrypoint!(process_instruction);
105///
106/// fn process_instruction(
107///     program_id: &Pubkey,
108///     accounts: &[AccountInfo],
109///     instruction_data: &[u8],
110/// ) -> ProgramResult {
111///     let account_info_iter = &mut accounts.iter();
112///
113///     let payer = next_account_info(account_info_iter)?;
114///     let recipient = next_account_info(account_info_iter)?;
115///     // The system program is a required account to invoke a system
116///     // instruction, even though we don't use it directly.
117///     let system_program_account = next_account_info(account_info_iter)?;
118///
119///     assert!(payer.is_writable);
120///     assert!(payer.is_signer);
121///     assert!(recipient.is_writable);
122///     assert!(system_program::check_id(system_program_account.key));
123///
124///     let lamports = 1000000;
125///
126///     invoke(
127///         &system_instruction::transfer(payer.key, recipient.key, lamports),
128///         &[payer.clone(), recipient.clone(), system_program_account.clone()],
129///     )
130/// }
131/// ```
132pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
133    invoke_signed(instruction, account_infos, &[])
134}
135
136/// Invoke a cross-program instruction but don't enforce Rust's aliasing rules.
137///
138/// This function is like [`invoke`] except that it does not check that
139/// [`RefCell`]s within [`AccountInfo`]s are properly borrowable as described in
140/// the documentation for that function. Those checks consume CPU cycles that
141/// this function avoids.
142///
143/// [`RefCell`]: std::cell::RefCell
144///
145/// # Safety
146///
147/// __This function is incorrectly missing an `unsafe` declaration.__
148///
149/// If any of the writable accounts passed to the callee contain data that is
150/// borrowed within the calling program, and that data is written to by the
151/// callee, then Rust's aliasing rules will be violated and cause undefined
152/// behavior.
153pub fn invoke_unchecked(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
154    invoke_signed_unchecked(instruction, account_infos, &[])
155}
156
157/// Invoke a cross-program instruction with program signatures.
158///
159/// This function is like [`invoke`] with the additional ability to virtually
160/// sign an invocation on behalf of one or more [program derived addresses][pda] (PDAs)
161/// controlled by the calling program, allowing the callee to mutate them, or
162/// otherwise confirm that a PDA program key has authorized the actions of the
163/// callee.
164///
165/// There is no cryptographic signing involved — PDA signing is a runtime
166/// construct that allows the calling program to control accounts as if it could
167/// cryptographically sign for them; and the callee to treat the account as if it
168/// was cryptographically signed.
169///
170/// The `signer_seeds` parameter is a slice of `u8` slices where the inner
171/// slices represent the seeds plus the _bump seed_ used to derive (with
172/// [`Pubkey::find_program_address`]) one of the PDAs within the `account_infos`
173/// slice of `AccountInfo`s. During invocation, the runtime will re-derive the
174/// PDA from the seeds and the calling program's ID, and if it matches one of
175/// the accounts in `account_info`, will consider that account "signed".
176///
177/// [pda]: https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses
178///
179/// See the documentation for [`Pubkey::find_program_address`] for more
180/// about program derived addresses.
181///
182/// # Examples
183///
184/// A simple example of creating an account for a PDA:
185///
186/// ```
187/// use miraland_program::{
188///     account_info::{next_account_info, AccountInfo},
189///     entrypoint,
190///     entrypoint::ProgramResult,
191///     program::invoke_signed,
192///     pubkey::Pubkey,
193///     system_instruction,
194///     system_program,
195/// };
196///
197/// entrypoint!(process_instruction);
198///
199/// fn process_instruction(
200///     program_id: &Pubkey,
201///     accounts: &[AccountInfo],
202///     instruction_data: &[u8],
203/// ) -> ProgramResult {
204///     let account_info_iter = &mut accounts.iter();
205///     let payer = next_account_info(account_info_iter)?;
206///     let vault_pda = next_account_info(account_info_iter)?;
207///     let system_program = next_account_info(account_info_iter)?;
208///
209///     assert!(payer.is_writable);
210///     assert!(payer.is_signer);
211///     assert!(vault_pda.is_writable);
212///     assert_eq!(vault_pda.owner, &system_program::ID);
213///     assert!(system_program::check_id(system_program.key));
214///
215///     let vault_bump_seed = instruction_data[0];
216///     let vault_seeds = &[b"vault", payer.key.as_ref(), &[vault_bump_seed]];
217///     let expected_vault_pda = Pubkey::create_program_address(vault_seeds, program_id)?;
218///
219///     assert_eq!(vault_pda.key, &expected_vault_pda);
220///
221///     let lamports = 10000000;
222///     let vault_size = 16;
223///
224///     invoke_signed(
225///         &system_instruction::create_account(
226///             &payer.key,
227///             &vault_pda.key,
228///             lamports,
229///             vault_size,
230///             &program_id,
231///         ),
232///         &[
233///             payer.clone(),
234///             vault_pda.clone(),
235///         ],
236///         &[
237///             &[
238///                 b"vault",
239///                 payer.key.as_ref(),
240///                 &[vault_bump_seed],
241///             ],
242///         ]
243///     )?;
244///     Ok(())
245/// }
246/// ```
247pub fn invoke_signed(
248    instruction: &Instruction,
249    account_infos: &[AccountInfo],
250    signers_seeds: &[&[&[u8]]],
251) -> ProgramResult {
252    // Check that the account RefCells are consistent with the request
253    for account_meta in instruction.accounts.iter() {
254        for account_info in account_infos.iter() {
255            if account_meta.pubkey == *account_info.key {
256                if account_meta.is_writable {
257                    let _ = account_info.try_borrow_mut_lamports()?;
258                    let _ = account_info.try_borrow_mut_data()?;
259                } else {
260                    let _ = account_info.try_borrow_lamports()?;
261                    let _ = account_info.try_borrow_data()?;
262                }
263                break;
264            }
265        }
266    }
267
268    invoke_signed_unchecked(instruction, account_infos, signers_seeds)
269}
270
271/// Invoke a cross-program instruction with signatures but don't enforce Rust's
272/// aliasing rules.
273///
274/// This function is like [`invoke_signed`] except that it does not check that
275/// [`RefCell`]s within [`AccountInfo`]s are properly borrowable as described in
276/// the documentation for that function. Those checks consume CPU cycles that
277/// this function avoids.
278///
279/// [`RefCell`]: std::cell::RefCell
280///
281/// # Safety
282///
283/// __This function is incorrectly missing an `unsafe` declaration.__
284///
285/// If any of the writable accounts passed to the callee contain data that is
286/// borrowed within the calling program, and that data is written to by the
287/// callee, then Rust's aliasing rules will be violated and cause undefined
288/// behavior.
289pub fn invoke_signed_unchecked(
290    instruction: &Instruction,
291    account_infos: &[AccountInfo],
292    signers_seeds: &[&[&[u8]]],
293) -> ProgramResult {
294    #[cfg(target_os = "solana")]
295    {
296        let instruction = StableInstruction::from(instruction.clone());
297        let result = unsafe {
298            crate::syscalls::sol_invoke_signed_rust(
299                &instruction as *const _ as *const u8,
300                account_infos as *const _ as *const u8,
301                account_infos.len() as u64,
302                signers_seeds as *const _ as *const u8,
303                signers_seeds.len() as u64,
304            )
305        };
306        match result {
307            crate::entrypoint::SUCCESS => Ok(()),
308            _ => Err(result.into()),
309        }
310    }
311
312    #[cfg(not(target_os = "solana"))]
313    crate::program_stubs::sol_invoke_signed(instruction, account_infos, signers_seeds)
314}
315
316/// Maximum size that can be set using [`set_return_data`].
317pub const MAX_RETURN_DATA: usize = 1024;
318
319/// Set the running program's return data.
320///
321/// Return data is a dedicated per-transaction buffer for data passed
322/// from cross-program invoked programs back to their caller.
323///
324/// The maximum size of return data is [`MAX_RETURN_DATA`]. Return data is
325/// retrieved by the caller with [`get_return_data`].
326pub fn set_return_data(data: &[u8]) {
327    #[cfg(target_os = "solana")]
328    unsafe {
329        crate::syscalls::sol_set_return_data(data.as_ptr(), data.len() as u64)
330    };
331
332    #[cfg(not(target_os = "solana"))]
333    crate::program_stubs::sol_set_return_data(data)
334}
335
336/// Get the return data from an invoked program.
337///
338/// For every transaction there is a single buffer with maximum length
339/// [`MAX_RETURN_DATA`], paired with a [`Pubkey`] representing the program ID of
340/// the program that most recently set the return data. Thus the return data is
341/// a global resource and care must be taken to ensure that it represents what
342/// is expected: called programs are free to set or not set the return data; and
343/// the return data may represent values set by programs multiple calls down the
344/// call stack, depending on the circumstances of transaction execution.
345///
346/// Return data is set by the callee with [`set_return_data`].
347///
348/// Return data is cleared before every CPI invocation — a program that
349/// has invoked no other programs can expect the return data to be `None`; if no
350/// return data was set by the previous CPI invocation, then this function
351/// returns `None`.
352///
353/// Return data is not cleared after returning from CPI invocations — a
354/// program that has called another program may retrieve return data that was
355/// not set by the called program, but instead set by a program further down the
356/// call stack; or, if a program calls itself recursively, it is possible that
357/// the return data was not set by the immediate call to that program, but by a
358/// subsequent recursive call to that program. Likewise, an external RPC caller
359/// may see return data that was not set by the program it is directly calling,
360/// but by a program that program called.
361///
362/// For more about return data see the [documentation for the return data proposal][rdp].
363///
364/// [rdp]: https://docs.solana.com/proposals/return-data
365pub fn get_return_data() -> Option<(Pubkey, Vec<u8>)> {
366    #[cfg(target_os = "solana")]
367    {
368        use std::cmp::min;
369
370        let mut buf = [0u8; MAX_RETURN_DATA];
371        let mut program_id = Pubkey::default();
372
373        let size = unsafe {
374            crate::syscalls::sol_get_return_data(
375                buf.as_mut_ptr(),
376                buf.len() as u64,
377                &mut program_id,
378            )
379        };
380
381        if size == 0 {
382            None
383        } else {
384            let size = min(size as usize, MAX_RETURN_DATA);
385            Some((program_id, buf[..size as usize].to_vec()))
386        }
387    }
388
389    #[cfg(not(target_os = "solana"))]
390    crate::program_stubs::sol_get_return_data()
391}
392
393/// Do sanity checks of type layout.
394#[doc(hidden)]
395#[allow(clippy::arithmetic_side_effects)]
396pub fn check_type_assumptions() {
397    extern crate memoffset;
398    use {
399        crate::{clock::Epoch, instruction::AccountMeta},
400        memoffset::offset_of,
401        std::{
402            cell::RefCell,
403            mem::{align_of, size_of},
404            rc::Rc,
405            str::FromStr,
406        },
407    };
408
409    // Code in this file assumes that u64 and usize are the same
410    assert_eq!(size_of::<u64>(), size_of::<usize>());
411    // Code in this file assumes that u8 is byte aligned
412    assert_eq!(1, align_of::<u8>());
413
414    // Enforce Instruction layout
415    {
416        assert_eq!(size_of::<AccountMeta>(), 32 + 1 + 1);
417
418        let pubkey1 = Pubkey::from_str("J9PYCcoKusHyKRMXnBL17VTXC3MVETyqBG2KyLXVv6Ai").unwrap();
419        let pubkey2 = Pubkey::from_str("Hvy4GHgPToZNoENTKjC4mJqpzWWjgTwXrFufKfxYiKkV").unwrap();
420        let pubkey3 = Pubkey::from_str("JDMyRL8rCkae7maCSv47upNuBMFd3Mgos1fz2AvYzVzY").unwrap();
421        let account_meta1 = AccountMeta {
422            pubkey: pubkey2,
423            is_signer: true,
424            is_writable: false,
425        };
426        let account_meta2 = AccountMeta {
427            pubkey: pubkey3,
428            is_signer: false,
429            is_writable: true,
430        };
431        let data = vec![1, 2, 3, 4, 5];
432        let instruction = Instruction {
433            program_id: pubkey1,
434            accounts: vec![account_meta1.clone(), account_meta2.clone()],
435            data: data.clone(),
436        };
437        let instruction = StableInstruction::from(instruction);
438        let instruction_addr = &instruction as *const _ as u64;
439
440        // program id
441        assert_eq!(offset_of!(StableInstruction, program_id), 48);
442        let pubkey_ptr = (instruction_addr + 48) as *const Pubkey;
443        unsafe {
444            assert_eq!(*pubkey_ptr, pubkey1);
445        }
446
447        // accounts
448        assert_eq!(offset_of!(StableInstruction, accounts), 0);
449        let accounts_ptr = (instruction_addr) as *const *const AccountMeta;
450        let accounts_cap = (instruction_addr + 8) as *const usize;
451        let accounts_len = (instruction_addr + 16) as *const usize;
452        unsafe {
453            assert_eq!(*accounts_cap, 2);
454            assert_eq!(*accounts_len, 2);
455            let account_meta_ptr = *accounts_ptr;
456            assert_eq!(*account_meta_ptr, account_meta1);
457            assert_eq!(*(account_meta_ptr.offset(1)), account_meta2);
458        }
459
460        // data
461        assert_eq!(offset_of!(StableInstruction, data), 24);
462        let data_ptr = (instruction_addr + 24) as *const *const [u8; 5];
463        let data_cap = (instruction_addr + 24 + 8) as *const usize;
464        let data_len = (instruction_addr + 24 + 16) as *const usize;
465        unsafe {
466            assert_eq!(*data_cap, 5);
467
468            assert_eq!(*data_len, 5);
469            let u8_ptr = *data_ptr;
470            assert_eq!(*u8_ptr, data[..]);
471        }
472    }
473
474    // Enforce AccountInfo layout
475    {
476        let key = Pubkey::from_str("6o8R9NsUxNskF1MfWM1f265y4w86JYbEwqCmTacdLkHp").unwrap();
477        let mut lamports = 31;
478        let mut data = vec![1, 2, 3, 4, 5];
479        let owner = Pubkey::from_str("2tjK4XyNU54XdN9jokx46QzLybbLVGwQQvTfhcuBXAjR").unwrap();
480        let account_info = AccountInfo {
481            key: &key,
482            is_signer: true,
483            is_writable: false,
484            lamports: Rc::new(RefCell::new(&mut lamports)),
485            data: Rc::new(RefCell::new(&mut data)),
486            owner: &owner,
487            executable: true,
488            rent_epoch: 42,
489        };
490        let account_info_addr = &account_info as *const _ as u64;
491
492        // key
493        assert_eq!(offset_of!(AccountInfo, key), 0);
494        let key_ptr = (account_info_addr) as *const &Pubkey;
495        unsafe {
496            assert_eq!(**key_ptr, key);
497        }
498
499        // lamports
500        assert_eq!(offset_of!(AccountInfo, lamports), 8);
501        let lamports_ptr = (account_info_addr + 8) as *const Rc<RefCell<&mut u64>>;
502        unsafe {
503            assert_eq!(**(*lamports_ptr).as_ptr(), 31);
504        }
505
506        // data
507        assert_eq!(offset_of!(AccountInfo, data), 16);
508        let data_ptr = (account_info_addr + 16) as *const Rc<RefCell<&mut [u8]>>;
509        unsafe {
510            assert_eq!((*(*data_ptr).as_ptr())[..], data[..]);
511        }
512
513        // owner
514        assert_eq!(offset_of!(AccountInfo, owner), 24);
515        let owner_ptr = (account_info_addr + 24) as *const &Pubkey;
516        unsafe {
517            assert_eq!(**owner_ptr, owner);
518        }
519
520        // rent_epoch
521        assert_eq!(offset_of!(AccountInfo, rent_epoch), 32);
522        let renbt_epoch_ptr = (account_info_addr + 32) as *const Epoch;
523        unsafe {
524            assert_eq!(*renbt_epoch_ptr, 42);
525        }
526
527        // is_signer
528        assert_eq!(offset_of!(AccountInfo, is_signer), 40);
529        let is_signer_ptr = (account_info_addr + 40) as *const bool;
530        unsafe {
531            assert!(*is_signer_ptr);
532        }
533
534        // is_writable
535        assert_eq!(offset_of!(AccountInfo, is_writable), 41);
536        let is_writable_ptr = (account_info_addr + 41) as *const bool;
537        unsafe {
538            assert!(!*is_writable_ptr);
539        }
540
541        // executable
542        assert_eq!(offset_of!(AccountInfo, executable), 42);
543        let executable_ptr = (account_info_addr + 42) as *const bool;
544        unsafe {
545            assert!(*executable_ptr);
546        }
547    }
548}
549
550#[cfg(test)]
551mod tests {
552    #[test]
553    fn test_check_type_assumptions() {
554        super::check_type_assumptions()
555    }
556}