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