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