solana_instruction_view/cpi.rs
1//! Cross-program invocation helpers.
2
3#[cfg(feature = "slice-cpi")]
4extern crate alloc;
5
6#[cfg(feature = "slice-cpi")]
7use alloc::boxed::Box;
8#[cfg(any(target_os = "solana", target_arch = "bpf"))]
9pub use solana_define_syscall::{
10 define_syscall,
11 definitions::{sol_get_return_data, sol_invoke_signed_c, sol_set_return_data},
12};
13use {
14 crate::InstructionView,
15 core::{marker::PhantomData, mem::MaybeUninit, ops::Deref, slice::from_raw_parts},
16 solana_account_view::AccountView,
17 solana_address::Address,
18 solana_program_error::{ProgramError, ProgramResult},
19};
20
21/// Maximum number of accounts allowed in `invoke` and `invoke_with_bounds`
22/// functions.
23pub const MAX_STATIC_CPI_ACCOUNTS: usize = 64;
24
25/// Maximum number of accounts allowed in a cross-program invocation.
26//
27// Note: This value will increase to 255 when SIMD-0339 is activated.
28pub const MAX_CPI_ACCOUNTS: usize = 128;
29
30/// An account for CPI invocations.
31///
32/// This struct contains the same information as an [`AccountView`], but has
33/// the memory layout as expected by `sol_invoke_signed_c` syscall.
34#[repr(C)]
35#[derive(Clone, Copy, Debug)]
36pub struct CpiAccount<'a> {
37 /// Address of the account.
38 address: *const Address,
39
40 /// Number of lamports owned by this account.
41 lamports: *const u64,
42
43 /// Length of data in bytes.
44 data_len: u64,
45
46 /// On-chain data within this account.
47 data: *const u8,
48
49 /// Program that owns this account.
50 owner: *const Address,
51
52 /// The epoch at which this account will next owe rent.
53 rent_epoch: u64,
54
55 /// Transaction was signed by this account's key?
56 is_signer: bool,
57
58 /// Is the account writable?
59 is_writable: bool,
60
61 /// This account's data contains a loaded program (and is now read-only).
62 executable: bool,
63
64 /// The pointers to the `AccountView` data are only valid for as long as the
65 /// `&'a AccountView` lives. Instead of holding a reference to the actual `AccountView`,
66 /// which would increase the size of the type, we claim to hold a reference without
67 /// actually holding one using a `PhantomData<&'a AccountView>`.
68 _account_view: PhantomData<&'a AccountView>,
69}
70
71impl<'a> From<&'a AccountView> for CpiAccount<'a> {
72 fn from(account: &'a AccountView) -> Self {
73 CpiAccount {
74 address: account.address(),
75 // SAFETY: Dereferencing `account.account_ptr()` to access its
76 // `lamports` field.
77 lamports: unsafe { &(*account.account_ptr()).lamports },
78 data_len: account.data_len() as u64,
79 data: account.data_ptr(),
80 // SAFETY: `account.owner()` is not expected to be updated between
81 // the creation of the CPI account and invocation of the program.
82 owner: unsafe { account.owner() },
83 // The `rent_epoch` field is not present in the `AccountView` struct,
84 // since the value occurs after the variable data of the account in
85 // the runtime input data.
86 rent_epoch: 0,
87 is_signer: account.is_signer(),
88 is_writable: account.is_writable(),
89 executable: account.executable(),
90 _account_view: PhantomData::<&'a AccountView>,
91 }
92 }
93}
94
95/// Represents a signer seed.
96///
97/// This struct contains the same information as a `[u8]`, but
98/// has the memory layout as expected by `sol_invoke_signed_c`
99/// syscall.
100#[repr(C)]
101#[derive(Debug, Clone)]
102pub struct Seed<'a> {
103 /// Seed bytes.
104 pub(crate) seed: *const u8,
105
106 /// Length of the seed bytes.
107 pub(crate) len: u64,
108
109 /// The pointer to the seed bytes is only valid while the `&'a [u8]` lives. Instead
110 /// of holding a reference to the actual `[u8]`, which would increase the size of the
111 /// type, we claim to hold a reference without actually holding one using a
112 /// `PhantomData<&'a [u8]>`.
113 _bytes: PhantomData<&'a [u8]>,
114}
115
116impl<'a> From<&'a [u8]> for Seed<'a> {
117 fn from(value: &'a [u8]) -> Self {
118 Self {
119 seed: value.as_ptr(),
120 len: value.len() as u64,
121 _bytes: PhantomData::<&[u8]>,
122 }
123 }
124}
125
126impl<'a, const SIZE: usize> From<&'a [u8; SIZE]> for Seed<'a> {
127 fn from(value: &'a [u8; SIZE]) -> Self {
128 Self {
129 seed: value.as_ptr(),
130 len: value.len() as u64,
131 _bytes: PhantomData::<&[u8]>,
132 }
133 }
134}
135
136impl Deref for Seed<'_> {
137 type Target = [u8];
138
139 fn deref(&self) -> &Self::Target {
140 unsafe { from_raw_parts(self.seed, self.len as usize) }
141 }
142}
143
144/// Represents a [program derived address][pda] (PDA) signer controlled by the
145/// calling program.
146///
147/// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
148#[repr(C)]
149#[derive(Debug, Clone)]
150pub struct Signer<'a, 'b> {
151 /// Signer seeds.
152 pub(crate) seeds: *const Seed<'a>,
153
154 /// Number of seeds.
155 pub(crate) len: u64,
156
157 /// The pointer to the seeds is only valid while the `&'b [Seed<'a>]` lives. Instead
158 /// of holding a reference to the actual `[Seed<'a>]`, which would increase the size
159 /// of the type, we claim to hold a reference without actually holding one using a
160 /// `PhantomData<&'b [Seed<'a>]>`.
161 _seeds: PhantomData<&'b [Seed<'a>]>,
162}
163
164impl<'a, 'b> From<&'b [Seed<'a>]> for Signer<'a, 'b> {
165 fn from(value: &'b [Seed<'a>]) -> Self {
166 Self {
167 seeds: value.as_ptr(),
168 len: value.len() as u64,
169 _seeds: PhantomData::<&'b [Seed<'a>]>,
170 }
171 }
172}
173
174impl<'a, 'b, const SIZE: usize> From<&'b [Seed<'a>; SIZE]> for Signer<'a, 'b> {
175 fn from(value: &'b [Seed<'a>; SIZE]) -> Self {
176 Self {
177 seeds: value.as_ptr(),
178 len: value.len() as u64,
179 _seeds: PhantomData::<&'b [Seed<'a>]>,
180 }
181 }
182}
183
184/// Convenience macro for constructing a `[Seed; N]` array from a list of seeds
185/// to create a [`Signer`].
186///
187/// # Example
188///
189/// Creating seeds array and signer for a PDA with a single seed and bump value:
190/// ```
191/// use solana_address::Address;
192/// use solana_instruction_view::{cpi::Signer, seeds};
193///
194/// let pda_bump = 0xffu8;
195/// let pda_ref = &[pda_bump];
196/// let example_key = Address::default();
197/// let seeds = seeds!(b"seed", example_key.as_ref(), pda_ref);
198/// let signer = Signer::from(&seeds);
199/// ```
200#[macro_export]
201macro_rules! seeds {
202 ( $($seed:expr),* ) => {
203 [$(
204 $crate::cpi::Seed::from($seed),
205 )*]
206 };
207}
208
209/// Invoke a cross-program instruction from an array of `AccountView`s.
210///
211/// This function is a convenience wrapper around the [`invoke_signed`] function
212/// with the signers' seeds set to an empty slice.
213///
214/// Note that this function is inlined to avoid the overhead of a function call,
215/// but uses stack memory allocation. When a large number of accounts is needed,
216/// it is recommended to use the [`invoke_with_slice`] function instead to reduce
217/// stack memory utilization.
218///
219/// # Important
220///
221/// The accounts on the `account_views` slice must be in the same order as the
222/// `accounts` field of the `instruction`. When the instruction has duplicated
223/// accounts, it is necessary to pass a duplicated reference to the same account
224/// to maintain the 1:1 relationship between accounts and instruction accounts.
225#[inline(always)]
226pub fn invoke<const ACCOUNTS: usize>(
227 instruction: &InstructionView,
228 account_views: &[&AccountView; ACCOUNTS],
229) -> ProgramResult {
230 invoke_signed::<ACCOUNTS>(instruction, account_views, &[])
231}
232
233/// Invoke a cross-program instruction from a slice of `AccountView`s.
234///
235/// This function is a convenience wrapper around the [`invoke_signed_with_bounds`]
236/// function with the signers' seeds set to an empty slice.
237///
238/// The `MAX_ACCOUNTS` constant defines the maximum number of accounts expected
239/// to be passed to the cross-program invocation. This provides an upper bound to
240/// the number of accounts that need to be statically allocated for cases where the
241/// number of instruction accounts is not known at compile time. The final number of
242/// accounts passed to the cross-program invocation will be the number of accounts
243/// required by the `instruction`, even if `MAX_ACCOUNTS` is greater than that. When
244/// `MAX_ACCOUNTS` is lower than the number of accounts expected by the instruction,
245/// this function will return a [`ProgramError::InvalidArgument`] error.
246///
247/// Note that this function is inlined to avoid the overhead of a function call,
248/// but uses stack memory allocation. When a large number of accounts is needed,
249/// it is recommended to use the [`invoke_with_slice`] function instead to reduce
250/// stack memory utilization.
251///
252/// # Important
253///
254/// The accounts on the `account_views` slice must be in the same order as the
255/// `accounts` field of the `instruction`. When the instruction has duplicated
256/// accounts, it is necessary to pass a duplicated reference to the same account
257/// to maintain the 1:1 relationship between accounts and instruction accounts.
258#[inline(always)]
259pub fn invoke_with_bounds<const MAX_ACCOUNTS: usize>(
260 instruction: &InstructionView,
261 account_views: &[&AccountView],
262) -> ProgramResult {
263 invoke_signed_with_bounds::<MAX_ACCOUNTS>(instruction, account_views, &[])
264}
265
266#[cfg(feature = "slice-cpi")]
267/// Invoke a cross-program instruction from a slice of `AccountView`s.
268///
269/// This function is a convenience wrapper around the [`invoke_signed_with_slice`]
270/// function with the signers' seeds set to an empty slice.
271///
272/// Note that this function will allocate heap memory to store up to
273/// `MAX_CPI_ACCOUNTS` accounts.
274///
275/// # Important
276///
277/// The accounts on the `account_views` slice must be in the same order as the
278/// `accounts` field of the `instruction`. When the instruction has duplicated
279/// accounts, it is necessary to pass a duplicated reference to the same account
280/// to maintain the 1:1 relationship between accounts and instruction accounts.
281#[inline(always)]
282pub fn invoke_with_slice(
283 instruction: &InstructionView,
284 account_views: &[&AccountView],
285) -> ProgramResult {
286 invoke_signed_with_slice(instruction, account_views, &[])
287}
288
289/// Invoke a cross-program instruction with signatures from an array of
290/// `AccountView`s.
291///
292/// This function performs validation of the `account_views` array to ensure that:
293/// 1. It has at least as many accounts as the number of accounts expected by
294/// the instruction.
295/// 2. The accounts match the expected accounts in the instruction, i.e., their
296/// `Address` matches the `address` in the `AccountView`.
297/// 3. The borrow state of the accounts is compatible with the mutability of the
298/// instruction accounts.
299///
300/// This validation is done to ensure that the borrow checker rules are followed,
301/// consuming CUs in the process. The [`invoke_signed_unchecked`] is an alternative
302/// to this function that have lower CU consumption since it does not perform
303/// any validation. This should only be used when the caller is sure that the borrow
304/// checker rules are followed.
305///
306/// Note that this function is inlined to avoid the overhead of a function call,
307/// but uses stack memory allocation. When a large number of accounts is needed,
308/// it is recommended to use the [`invoke_signed_with_slice`] function instead
309/// to reduce stack memory utilization.
310///
311/// # Important
312///
313/// The accounts on the `account_views` array must be in the same order as the
314/// `accounts` field of the `instruction`. When the instruction has duplicated
315/// accounts, it is necessary to pass a duplicated reference to the same account
316/// to maintain the 1:1 relationship between accounts and instruction accounts.
317#[inline(always)]
318pub fn invoke_signed<const ACCOUNTS: usize>(
319 instruction: &InstructionView,
320 account_views: &[&AccountView; ACCOUNTS],
321 signers_seeds: &[Signer],
322) -> ProgramResult {
323 // Check that the number of `ACCOUNTS` provided is not greater than
324 // the maximum number of accounts allowed.
325 const {
326 assert!(
327 ACCOUNTS <= MAX_STATIC_CPI_ACCOUNTS,
328 "ACCOUNTS is greater than allowed MAX_STATIC_CPI_ACCOUNTS"
329 );
330 }
331
332 const UNINIT: MaybeUninit<CpiAccount> = MaybeUninit::<CpiAccount>::uninit();
333 let mut accounts = [UNINIT; ACCOUNTS];
334
335 // SAFETY: The array of `AccountView`s will be checked to ensure that it has
336 // the same number of accounts as the instruction – this indirectly validates
337 // that the stack allocated account storage `ACCOUNTS` is sufficient for the
338 // number of accounts expected by the instruction.
339 unsafe {
340 inner_invoke_signed_with_slice(instruction, account_views, &mut accounts, signers_seeds)
341 }
342}
343
344/// Invoke a cross-program instruction with signatures from a slice of
345/// `AccountView`s.
346///
347/// This function performs validation of the `account_views` slice to ensure that:
348/// 1. It has at least as many accounts as the number of accounts expected by
349/// the instruction.
350/// 2. The accounts match the expected accounts in the instruction, i.e., their
351/// `Address` matches the `address` in the `AccountView`.
352/// 3. The borrow state of the accounts is compatible with the mutability of the
353/// instruction accounts.
354///
355/// This validation is done to ensure that the borrow checker rules are followed,
356/// consuming CUs in the process. The [`invoke_signed_unchecked`] is an alternative
357/// to this function that has lower CU consumption since it does not perform
358/// any validation. This should only be used when the caller is sure that the borrow
359/// checker rules are followed.
360///
361/// The `MAX_ACCOUNTS` constant defines the maximum number of accounts expected
362/// to be passed to the cross-program invocation. This provides an upper bound to
363/// the number of accounts that need to be statically allocated for cases where the
364/// number of instruction accounts is not known at compile time. The final number of
365/// accounts passed to the cross-program invocation will be the number of accounts
366/// required by the `instruction`, even if `MAX_ACCOUNTS` is greater than that. When
367/// `MAX_ACCOUNTS` is lower than the number of accounts expected by the instruction,
368/// this function will return a [`ProgramError::InvalidArgument`] error.
369///
370/// Note that this function is inlined to avoid the overhead of a function call,
371/// but uses stack memory allocation. When a large number of accounts is needed,
372/// it is recommended to use the [`invoke_signed_with_slice`] function instead to reduce
373/// stack memory utilization.
374///
375/// # Important
376///
377/// The accounts on the `account_views` slice must be in the same order as the
378/// `accounts` field of the `instruction`. When the instruction has duplicated
379/// accounts, it is necessary to pass a duplicated reference to the same account
380/// to maintain the 1:1 relationship between accounts and instruction accounts.
381#[inline(always)]
382pub fn invoke_signed_with_bounds<const MAX_ACCOUNTS: usize>(
383 instruction: &InstructionView,
384 account_views: &[&AccountView],
385 signers_seeds: &[Signer],
386) -> ProgramResult {
387 // Check that the number of `MAX_ACCOUNTS` provided is not greater than
388 // the maximum number of static accounts allowed.
389 const {
390 assert!(
391 MAX_ACCOUNTS <= MAX_STATIC_CPI_ACCOUNTS,
392 "MAX_ACCOUNTS is greater than allowed MAX_STATIC_CPI_ACCOUNTS"
393 );
394 }
395
396 // Check that the stack allocated account storage `MAX_ACCOUNTS` is sufficient
397 // for the number of accounts expected by the instruction.
398 if MAX_ACCOUNTS < instruction.accounts.len() {
399 return Err(ProgramError::InvalidArgument);
400 }
401
402 const UNINIT: MaybeUninit<CpiAccount> = MaybeUninit::<CpiAccount>::uninit();
403 let mut accounts = [UNINIT; MAX_ACCOUNTS];
404
405 // SAFETY: The stack allocated account storage `MAX_ACCOUNTS` was validated
406 // to be sufficient for the number of accounts expected by the instruction.
407 unsafe {
408 inner_invoke_signed_with_slice(instruction, account_views, &mut accounts, signers_seeds)
409 }
410}
411
412#[cfg(feature = "slice-cpi")]
413/// Invoke a cross-program instruction with signatures from a slice of
414/// `AccountView`s.
415///
416/// This function performs validation of the `account_views` slice to ensure that:
417/// 1. It has at least as many accounts as the number of accounts expected by
418/// the instruction.
419/// 2. The accounts match the expected accounts in the instruction, i.e., their
420/// `Address` matches the `address` in the `AccountView`.
421/// 3. The borrow state of the accounts is compatible with the mutability of the
422/// instruction accounts.
423///
424/// This validation is done to ensure that the borrow checker rules are followed,
425/// consuming CUs in the process. The [`invoke_signed_unchecked`] is an alternative
426/// to this function that have lower CU consumption since it does not perform
427/// any validation. This should only be used when the caller is sure that the borrow
428/// checker rules are followed.
429///
430/// Note that this function will allocate heap memory to store up to
431/// `MAX_CPI_ACCOUNTS` accounts.
432///
433/// # Important
434///
435/// The accounts on the `account_views` slice must be in the same order as the
436/// `accounts` field of the `instruction`. When the instruction has duplicated
437/// accounts, it is necessary to pass a duplicated reference to the same account
438/// to maintain the 1:1 relationship between accounts and instruction accounts.
439#[inline(always)]
440pub fn invoke_signed_with_slice(
441 instruction: &InstructionView,
442 account_views: &[&AccountView],
443 signers_seeds: &[Signer],
444) -> ProgramResult {
445 // Check that the number of instruction accounts does not exceed
446 // the maximum allowed number of CPI accounts.
447 if MAX_CPI_ACCOUNTS < instruction.accounts.len() {
448 return Err(ProgramError::InvalidArgument);
449 }
450
451 let mut accounts = Box::<[CpiAccount]>::new_uninit_slice(instruction.accounts.len());
452
453 // SAFETY: The allocated `accounts` slice has the same size as the expected number
454 // of instruction accounts.
455 unsafe {
456 inner_invoke_signed_with_slice(instruction, account_views, &mut accounts, signers_seeds)
457 }
458}
459
460/// Internal function to invoke a cross-program instruction with signatures
461/// from a slice of `AccountView`s performing borrow checking.
462///
463/// This function performs validation of the `account_views` slice to ensure that:
464/// 1. It has at least as many accounts as the number of accounts expected by
465/// the instruction.
466/// 2. The accounts match the expected accounts in the instruction, i.e., their
467/// `Address` matches the `address` in the `AccountView`.
468/// 3. The borrow state of the accounts is compatible with the mutability of the
469/// instruction accounts.
470///
471/// # Safety
472///
473/// This function is unsafe because it does not check that `accounts` is sufficiently
474/// large for the number of accounts expected by the instruction. Using an `accounts` slice
475/// shorter than the number of accounts expected by the instruction will result in
476/// undefined behavior.
477#[inline(always)]
478unsafe fn inner_invoke_signed_with_slice<'account, 'cpi>(
479 instruction: &InstructionView,
480 account_views: &[&'account AccountView],
481 accounts: &mut [MaybeUninit<CpiAccount<'cpi>>],
482 signers_seeds: &[Signer],
483) -> ProgramResult
484where
485 'account: 'cpi,
486{
487 // Check that the number of accounts provided is not less than
488 // the number of accounts expected by the instruction.
489 if account_views.len() < instruction.accounts.len() {
490 return Err(ProgramError::NotEnoughAccountKeys);
491 }
492
493 account_views
494 .iter()
495 .zip(instruction.accounts.iter())
496 .zip(accounts.iter_mut())
497 .try_for_each(|((account_view, instruction_account), account)| {
498 // In order to check whether the borrow state is compatible
499 // with the invocation, we need to check that we have the
500 // correct account view and instruction account pair.
501 if account_view.address() != instruction_account.address {
502 return Err(ProgramError::InvalidArgument);
503 }
504
505 // Determines the borrow state that would be invalid according
506 // to their mutability on the instruction.
507 let borrowed = if instruction_account.is_writable {
508 // If the account is required to be writable, it cannot
509 // be currently borrowed.
510 account_view.is_borrowed()
511 } else {
512 // If the account is required to be read-only, it cannot
513 // be currently mutably borrowed.
514 account_view.is_borrowed_mut()
515 };
516
517 if borrowed {
518 return Err(ProgramError::AccountBorrowFailed);
519 }
520
521 account.write(CpiAccount::from(*account_view));
522
523 Ok(())
524 })?;
525
526 // SAFETY: At this point it is guaranteed that instruction accounts are
527 // borrowable according to their mutability on the instruction.
528 unsafe {
529 invoke_signed_unchecked(
530 instruction,
531 from_raw_parts(accounts.as_ptr() as _, instruction.accounts.len()),
532 signers_seeds,
533 );
534 }
535
536 Ok(())
537}
538
539/// Invoke a cross-program instruction but don't enforce Rust's aliasing rules.
540///
541/// This function does not check that [`CpiAccount`]s are properly borrowable.
542/// Those checks consume CUs that this function avoids.
543///
544/// Note that the maximum number of accounts that can be passed to a cross-program
545/// invocation is defined by the `MAX_CPI_ACCOUNTS` constant. Even if the `[CpiAccount]`
546/// slice has more accounts, only the number of accounts required by the `instruction`
547/// will be used.
548///
549/// # Safety
550///
551/// If any of the writable accounts passed to the callee contain data that is
552/// borrowed within the calling program, and that data is written to by the
553/// callee, then Rust's aliasing rules will be violated and cause undefined
554/// behavior.
555#[inline(always)]
556pub unsafe fn invoke_unchecked(instruction: &InstructionView, accounts: &[CpiAccount]) {
557 invoke_signed_unchecked(instruction, accounts, &[])
558}
559
560/// Invoke a cross-program instruction with signatures but don't enforce Rust's
561/// aliasing rules.
562///
563/// This function does not check that [`CpiAccount`]s are properly borrowable.
564/// Those checks consume CUs that this function avoids.
565///
566/// Note that the maximum number of accounts that can be passed to a cross-program
567/// invocation is defined by the `MAX_CPI_ACCOUNTS` constant. Even if the `[CpiAccount]`
568/// slice has more accounts, only the number of accounts required by the `instruction`
569/// will be used.
570///
571/// # Safety
572///
573/// If any of the writable accounts passed to the callee contain data that is
574/// borrowed within the calling program, and that data is written to by the
575/// callee, then Rust's aliasing rules will be violated and cause undefined
576/// behavior.
577#[inline(always)]
578pub unsafe fn invoke_signed_unchecked(
579 instruction: &InstructionView,
580 accounts: &[CpiAccount],
581 signers_seeds: &[Signer],
582) {
583 #[cfg(any(target_os = "solana", target_arch = "bpf"))]
584 {
585 use crate::InstructionAccount;
586
587 /// An `Instruction` as expected by `sol_invoke_signed_c`.
588 ///
589 /// DO NOT EXPOSE THIS STRUCT:
590 ///
591 /// To ensure pointers are valid upon use, the scope of this struct should
592 /// only be limited to the stack where `sol_invoke_signed_c` happens and then
593 /// discarded immediately after.
594 #[repr(C)]
595 struct CInstruction<'a> {
596 /// Public key of the program.
597 program_id: *const Address,
598
599 /// Accounts expected by the program instruction.
600 accounts: *const InstructionAccount<'a>,
601
602 /// Number of accounts expected by the program instruction.
603 accounts_len: u64,
604
605 /// Data expected by the program instruction.
606 data: *const u8,
607
608 /// Length of the data expected by the program instruction.
609 data_len: u64,
610 }
611
612 let cpi_instruction = CInstruction {
613 program_id: instruction.program_id,
614 accounts: instruction.accounts.as_ptr(),
615 accounts_len: instruction.accounts.len() as u64,
616 data: instruction.data.as_ptr(),
617 data_len: instruction.data.len() as u64,
618 };
619
620 unsafe {
621 sol_invoke_signed_c(
622 &cpi_instruction as *const _ as *const u8,
623 accounts as *const _ as *const u8,
624 accounts.len() as u64,
625 signers_seeds as *const _ as *const u8,
626 signers_seeds.len() as u64,
627 )
628 };
629 }
630
631 #[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
632 core::hint::black_box((instruction, accounts, signers_seeds));
633}
634
635/// Maximum size that can be set using [`set_return_data`].
636pub const MAX_RETURN_DATA: usize = 1024;
637
638/// Set the running program's return data.
639///
640/// Return data is a dedicated per-transaction buffer for data passed
641/// from cross-program invoked programs back to their caller.
642///
643/// The maximum size of return data is [`MAX_RETURN_DATA`]. Return data is
644/// retrieved by the caller with [`get_return_data`].
645#[inline(always)]
646pub fn set_return_data(data: &[u8]) {
647 #[cfg(any(target_os = "solana", target_arch = "bpf"))]
648 unsafe {
649 sol_set_return_data(data.as_ptr(), data.len() as u64)
650 };
651
652 #[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
653 core::hint::black_box(data);
654}
655
656/// Get the return data from an invoked program.
657///
658/// For every transaction there is a single buffer with maximum length
659/// [`MAX_RETURN_DATA`], paired with an [`Address`] representing the program ID of
660/// the program that most recently set the return data. Thus the return data is
661/// a global resource and care must be taken to ensure that it represents what
662/// is expected: called programs are free to set or not set the return data; and
663/// the return data may represent values set by programs multiple calls down the
664/// call stack, depending on the circumstances of transaction execution.
665///
666/// Return data is set by the callee with [`set_return_data`].
667///
668/// Return data is cleared before every CPI invocation - a program that
669/// has invoked no other programs can expect the return data to be `None`; if no
670/// return data was set by the previous CPI invocation, then this function
671/// returns `None`.
672///
673/// Return data is not cleared after returning from CPI invocations. A
674/// program that has called another program may retrieve return data that was
675/// not set by the called program, but instead set by a program further down the
676/// call stack; or, if a program calls itself recursively, it is possible that
677/// the return data was not set by the immediate call to that program, but by a
678/// subsequent recursive call to that program. Likewise, an external RPC caller
679/// may see return data that was not set by the program it is directly calling,
680/// but by a program that program called.
681///
682/// For more about return data see the [documentation for the return data proposal][rdp].
683///
684/// [rdp]: https://docs.solanalabs.com/proposals/return-data
685#[inline]
686pub fn get_return_data() -> Option<ReturnData> {
687 #[cfg(any(target_os = "solana", target_arch = "bpf"))]
688 {
689 const UNINIT_BYTE: MaybeUninit<u8> = MaybeUninit::<u8>::uninit();
690 let mut data = [UNINIT_BYTE; MAX_RETURN_DATA];
691 let mut program_id = MaybeUninit::<Address>::uninit();
692
693 let size = unsafe {
694 sol_get_return_data(
695 data.as_mut_ptr() as *mut u8,
696 data.len() as u64,
697 program_id.as_mut_ptr() as *mut _ as *mut u8,
698 )
699 };
700
701 if size == 0 {
702 None
703 } else {
704 Some(ReturnData {
705 program_id: unsafe { program_id.assume_init() },
706 data,
707 size: core::cmp::min(size as usize, MAX_RETURN_DATA),
708 })
709 }
710 }
711
712 #[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
713 core::hint::black_box(None)
714}
715
716/// Struct to hold the return data from an invoked program.
717#[derive(Debug)]
718pub struct ReturnData {
719 /// Program that most recently set the return data.
720 program_id: Address,
721
722 /// Return data set by the program.
723 data: [MaybeUninit<u8>; MAX_RETURN_DATA],
724
725 /// Length of the return data.
726 size: usize,
727}
728
729impl ReturnData {
730 /// Returns the program that most recently set the return data.
731 pub fn program_id(&self) -> &Address {
732 &self.program_id
733 }
734
735 /// Return the data set by the program.
736 pub fn as_slice(&self) -> &[u8] {
737 unsafe { from_raw_parts(self.data.as_ptr() as _, self.size) }
738 }
739}