solana_cpi/
lib.rs

1//! Cross-program invocation.
2//!
3//! Solana programs may call other programs, termed [_cross-program
4//! invocations_][cpi] (CPI), with the [`invoke`] and [`invoke_signed`]
5//! functions.
6//!
7//! This crate does not support overwriting syscall stubs for offchain code.
8//! If you want to overwrite syscall stubs, use the wrapper functions in
9//! [`solana_program::program`].
10//!
11//! [`invoke`]: invoke
12//! [`invoke_signed`]: invoke_signed
13//! [cpi]: https://solana.com/docs/core/cpi
14//! [`solana_program::program`]: https://docs.rs/solana-program/latest/solana_program/program/
15#![cfg_attr(docsrs, feature(doc_cfg))]
16
17use {
18    solana_account_info::AccountInfo, solana_instruction::Instruction,
19    solana_program_error::ProgramResult, solana_pubkey::Pubkey,
20};
21#[cfg(target_os = "solana")]
22pub mod syscalls;
23
24/// Invoke a cross-program instruction.
25///
26/// Invoking one program from another program requires an [`Instruction`]
27/// containing the program ID of the other program, instruction data that
28/// will be understood by the other program, and a list of [`AccountInfo`]s
29/// corresponding to all of the accounts accessed by the other program. Because
30/// the only way for a program to acquire `AccountInfo` values is by receiving
31/// them from the runtime at the [program entrypoint][entrypoint!], any account
32/// required by the callee program must transitively be required by the caller
33/// program, and provided by _its_ caller. The same is true of the program ID of
34/// the called program.
35///
36/// [entrypoint!]: https://docs.rs/solana-entrypoint/latest/solana_entrypoint/macro.entrypoint.html
37///
38/// The `Instruction` is usually built from within the calling program, but may
39/// be deserialized from an external source.
40///
41/// This function will not return if the called program returns anything other
42/// than success. If the callee returns an error or aborts then the entire
43/// transaction will immediately fail. To return data as the result of a
44/// cross-program invocation use the [`set_return_data`] / [`get_return_data`]
45/// functions, or have the callee write to a dedicated account for that purpose.
46///
47/// A program may directly call itself recursively, but may not be indirectly
48/// called recursively (reentered) by another program. Indirect reentrancy will
49/// cause the transaction to immediately fail.
50///
51/// # Validation of shared data between programs
52///
53/// The `AccountInfo` structures passed to this function contain data that is
54/// directly accessed by the runtime and is copied to and from the memory space
55/// of the called program. Some of that data, the [`AccountInfo::lamports`] and
56/// [`AccountInfo::data`] fields, may be mutated as a side-effect of the called
57/// program, if that program has writable access to the given account.
58///
59/// These two fields are stored in [`RefCell`]s to enforce the aliasing
60/// discipline for mutated values required by the Rust language. Prior to
61/// invoking the runtime, this routine will test that each `RefCell` is
62/// borrowable as required by the callee and return an error if not.
63///
64/// The CPU cost of these runtime checks can be avoided with the unsafe
65/// [`invoke_unchecked`] function.
66///
67/// [`RefCell`]: std::cell::RefCell
68///
69/// # Errors
70///
71/// If the called program completes successfully and violates no runtime
72/// invariants, then this function will return successfully. If the callee
73/// completes and returns a [`ProgramError`], then the transaction will
74/// immediately fail. Control will not return to the caller.
75///
76/// Various runtime invariants are checked before the callee is invoked and
77/// before returning control to the caller. If any of these invariants are
78/// violated then the transaction will immediately fail. A non-exhaustive list
79/// of these invariants includes:
80///
81/// - The sum of lamports owned by all referenced accounts has not changed.
82/// - A program has not debited lamports from an account it does not own.
83/// - A program has not otherwise written to an account that it does not own.
84/// - A program has not written to an account that is not writable.
85/// - The size of account data has not exceeded applicable limits.
86///
87/// If the invoked program does not exist or is not executable then
88/// the transaction will immediately fail.
89///
90/// If any of the `RefCell`s within the provided `AccountInfo`s cannot be
91/// borrowed in accordance with the call's requirements, an error of
92/// [`ProgramError::AccountBorrowFailed`] is returned.
93///
94/// [`ProgramError`]: https://docs.rs/solana-program-error/latest/solana_program_error/enum.ProgramError.html
95/// [`ProgramError::AccountBorrowFailed`]: https://docs.rs/solana-program-error/latest/solana_program_error/enum.ProgramError.html#variant.AccountBorrowFailed
96///
97/// # Examples
98///
99/// A simple example of transferring lamports via CPI:
100///
101/// ```
102/// use solana_cpi::invoke;
103/// use solana_account_info::{next_account_info, AccountInfo};
104/// use solana_program_entrypoint::entrypoint;
105/// use solana_program_error::ProgramResult;
106/// use solana_pubkey::Pubkey;
107/// use solana_sdk_ids::system_program;
108/// use solana_system_interface::instruction as system_instruction;
109///
110/// entrypoint!(process_instruction);
111///
112/// fn process_instruction(
113///     program_id: &Pubkey,
114///     accounts: &[AccountInfo],
115///     instruction_data: &[u8],
116/// ) -> ProgramResult {
117///     let account_info_iter = &mut accounts.iter();
118///
119///     let payer = next_account_info(account_info_iter)?;
120///     let recipient = next_account_info(account_info_iter)?;
121///     // The system program is a required account to invoke a system
122///     // instruction, even though we don't use it directly.
123///     let system_program_account = next_account_info(account_info_iter)?;
124///
125///     assert!(payer.is_writable);
126///     assert!(payer.is_signer);
127///     assert!(recipient.is_writable);
128///     assert!(system_program::check_id(system_program_account.key));
129///
130///     let lamports = 1000000;
131///
132///     invoke(
133///         &system_instruction::transfer(payer.key, recipient.key, lamports),
134///         &[payer.clone(), recipient.clone(), system_program_account.clone()],
135///     )
136/// }
137/// ```
138pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
139    invoke_signed(instruction, account_infos, &[])
140}
141
142/// Invoke a cross-program instruction but don't enforce Rust's aliasing rules.
143///
144/// This function is like [`invoke`] except that it does not check that
145/// [`RefCell`]s within [`AccountInfo`]s are properly borrowable as described in
146/// the documentation for that function. Those checks consume CPU cycles that
147/// this function avoids.
148///
149/// [`RefCell`]: std::cell::RefCell
150///
151/// # Safety
152///
153/// __This function is incorrectly missing an `unsafe` declaration.__
154///
155/// If any of the writable accounts passed to the callee contain data that is
156/// borrowed within the calling program, and that data is written to by the
157/// callee, then Rust's aliasing rules will be violated and cause undefined
158/// behavior.
159pub fn invoke_unchecked(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
160    invoke_signed_unchecked(instruction, account_infos, &[])
161}
162
163/// Invoke a cross-program instruction with program signatures.
164///
165/// This function is like [`invoke`] with the additional ability to virtually
166/// sign an invocation on behalf of one or more [program derived addresses][pda] (PDAs)
167/// controlled by the calling program, allowing the callee to mutate them, or
168/// otherwise confirm that a PDA program key has authorized the actions of the
169/// callee.
170///
171/// There is no cryptographic signing involved — PDA signing is a runtime
172/// construct that allows the calling program to control accounts as if it could
173/// cryptographically sign for them; and the callee to treat the account as if it
174/// was cryptographically signed.
175///
176/// The `signer_seeds` parameter is a slice of `u8` slices where the inner
177/// slices represent the seeds plus the _bump seed_ used to derive (with
178/// [`Pubkey::find_program_address`]) one of the PDAs within the `account_infos`
179/// slice of `AccountInfo`s. During invocation, the runtime will re-derive the
180/// PDA from the seeds and the calling program's ID, and if it matches one of
181/// the accounts in `account_info`, will consider that account "signed".
182///
183/// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses
184/// [`Pubkey::find_program_address`]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address
185///
186/// See the documentation for [`Pubkey::find_program_address`] for more
187/// about program derived addresses.
188///
189/// # Examples
190///
191/// A simple example of creating an account for a PDA:
192///
193/// ```
194/// use solana_cpi::invoke_signed;
195/// use solana_account_info::{next_account_info, AccountInfo};
196/// use solana_program_entrypoint::entrypoint;
197/// use solana_program_error::ProgramResult;
198/// use solana_pubkey::Pubkey;
199/// use solana_sdk_ids::system_program;
200/// use solana_system_interface::instruction as system_instruction;
201///
202/// entrypoint!(process_instruction);
203///
204/// fn process_instruction(
205///     program_id: &Pubkey,
206///     accounts: &[AccountInfo],
207///     instruction_data: &[u8],
208/// ) -> ProgramResult {
209///     let account_info_iter = &mut accounts.iter();
210///     let payer = next_account_info(account_info_iter)?;
211///     let vault_pda = next_account_info(account_info_iter)?;
212///     let system_program = next_account_info(account_info_iter)?;
213///
214///     assert!(payer.is_writable);
215///     assert!(payer.is_signer);
216///     assert!(vault_pda.is_writable);
217///     assert_eq!(vault_pda.owner, &system_program::ID);
218///     assert!(system_program::check_id(system_program.key));
219///
220///     let vault_bump_seed = instruction_data[0];
221///     let vault_seeds = &[b"vault", payer.key.as_ref(), &[vault_bump_seed]];
222///     let expected_vault_pda = Pubkey::create_program_address(vault_seeds, program_id)?;
223///
224///     assert_eq!(vault_pda.key, &expected_vault_pda);
225///
226///     let lamports = 10000000;
227///     let vault_size = 16;
228///
229///     invoke_signed(
230///         &system_instruction::create_account(
231///             &payer.key,
232///             &vault_pda.key,
233///             lamports,
234///             vault_size,
235///             &program_id,
236///         ),
237///         &[
238///             payer.clone(),
239///             vault_pda.clone(),
240///         ],
241///         &[
242///             &[
243///                 b"vault",
244///                 payer.key.as_ref(),
245///                 &[vault_bump_seed],
246///             ],
247///         ]
248///     )?;
249///     Ok(())
250/// }
251/// ```
252pub fn invoke_signed(
253    instruction: &Instruction,
254    account_infos: &[AccountInfo],
255    signers_seeds: &[&[&[u8]]],
256) -> ProgramResult {
257    // Check that the account RefCells are consistent with the request
258    for account_meta in instruction.accounts.iter() {
259        for account_info in account_infos.iter() {
260            if account_meta.pubkey == *account_info.key {
261                if account_meta.is_writable {
262                    let _ = account_info.try_borrow_mut_lamports()?;
263                    let _ = account_info.try_borrow_mut_data()?;
264                } else {
265                    let _ = account_info.try_borrow_lamports()?;
266                    let _ = account_info.try_borrow_data()?;
267                }
268                break;
269            }
270        }
271    }
272
273    invoke_signed_unchecked(instruction, account_infos, signers_seeds)
274}
275
276/// Copied from `solana_program_entrypoint::SUCCESS`
277/// to avoid a `solana_program_entrypoint` dependency
278const _SUCCESS: u64 = 0;
279#[cfg(test)]
280static_assertions::const_assert_eq!(_SUCCESS, solana_program_entrypoint::SUCCESS);
281
282/// Invoke a cross-program instruction with signatures but don't enforce Rust's
283/// aliasing rules.
284///
285/// This function is like [`invoke_signed`] except that it does not check that
286/// [`RefCell`]s within [`AccountInfo`]s are properly borrowable as described in
287/// the documentation for that function. Those checks consume CPU cycles that
288/// this function avoids.
289///
290/// [`RefCell`]: std::cell::RefCell
291///
292/// # Safety
293///
294/// __This function is incorrectly missing an `unsafe` declaration.__
295///
296/// If any of the writable accounts passed to the callee contain data that is
297/// borrowed within the calling program, and that data is written to by the
298/// callee, then Rust's aliasing rules will be violated and cause undefined
299/// behavior.
300#[allow(unused_variables)]
301pub fn invoke_signed_unchecked(
302    instruction: &Instruction,
303    account_infos: &[AccountInfo],
304    signers_seeds: &[&[&[u8]]],
305) -> ProgramResult {
306    #[cfg(target_os = "solana")]
307    {
308        let instruction =
309            solana_stable_layout::stable_instruction::StableInstruction::from(instruction.clone());
310        let result = unsafe {
311            crate::syscalls::sol_invoke_signed_rust(
312                &instruction as *const _ as *const u8,
313                account_infos as *const _ as *const u8,
314                account_infos.len() as u64,
315                signers_seeds as *const _ as *const u8,
316                signers_seeds.len() as u64,
317            )
318        };
319        match result {
320            _SUCCESS => Ok(()),
321            _ => Err(result.into()),
322        }
323    }
324
325    #[cfg(not(target_os = "solana"))]
326    Ok(())
327}
328
329/// Maximum size that can be set using [`set_return_data`].
330pub const MAX_RETURN_DATA: usize = 1024;
331
332/// Set the running program's return data.
333///
334/// Return data is a dedicated per-transaction buffer for data passed
335/// from cross-program invoked programs back to their caller.
336///
337/// The maximum size of return data is [`MAX_RETURN_DATA`]. Return data is
338/// retrieved by the caller with [`get_return_data`].
339#[allow(unused_variables)]
340pub fn set_return_data(data: &[u8]) {
341    #[cfg(target_os = "solana")]
342    unsafe {
343        crate::syscalls::sol_set_return_data(data.as_ptr(), data.len() as u64)
344    };
345}
346
347/// Get the return data from an invoked program.
348///
349/// For every transaction there is a single buffer with maximum length
350/// [`MAX_RETURN_DATA`], paired with a [`Pubkey`] representing the program ID of
351/// the program that most recently set the return data. Thus the return data is
352/// a global resource and care must be taken to ensure that it represents what
353/// is expected: called programs are free to set or not set the return data; and
354/// the return data may represent values set by programs multiple calls down the
355/// call stack, depending on the circumstances of transaction execution.
356///
357/// Return data is set by the callee with [`set_return_data`].
358///
359/// Return data is cleared before every CPI invocation — a program that
360/// has invoked no other programs can expect the return data to be `None`; if no
361/// return data was set by the previous CPI invocation, then this function
362/// returns `None`.
363///
364/// Return data is not cleared after returning from CPI invocations — a
365/// program that has called another program may retrieve return data that was
366/// not set by the called program, but instead set by a program further down the
367/// call stack; or, if a program calls itself recursively, it is possible that
368/// the return data was not set by the immediate call to that program, but by a
369/// subsequent recursive call to that program. Likewise, an external RPC caller
370/// may see return data that was not set by the program it is directly calling,
371/// but by a program that program called.
372///
373/// For more about return data see the [documentation for the return data proposal][rdp].
374///
375/// [rdp]: https://docs.solanalabs.com/proposals/return-data
376pub fn get_return_data() -> Option<(Pubkey, Vec<u8>)> {
377    #[cfg(target_os = "solana")]
378    {
379        use std::cmp::min;
380
381        let mut buf = [0u8; MAX_RETURN_DATA];
382        let mut program_id = Pubkey::default();
383
384        let size = unsafe {
385            solana_define_syscall::definitions::sol_get_return_data(
386                buf.as_mut_ptr(),
387                buf.len() as u64,
388                &mut program_id as *mut _ as *mut u8,
389            )
390        };
391
392        if size == 0 {
393            None
394        } else {
395            let size = min(size as usize, MAX_RETURN_DATA);
396            Some((program_id, buf[..size as usize].to_vec()))
397        }
398    }
399
400    #[cfg(not(target_os = "solana"))]
401    None
402}