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}