pub fn invoke(
instruction: &Instruction,
account_infos: &[AccountInfo<'_>],
) -> ProgramResult
Expand description
Invoke a cross-program instruction.
Invoking one program from another program requires an Instruction
containing the program ID of the other program, instruction data that
will be understood by the other program, and a list of AccountInfo
s
corresponding to all of the accounts accessed by the other program. Because
the only way for a program to acquire AccountInfo
values is by receiving
them from the runtime at the program entrypoint, any account
required by the callee program must transitively be required by the caller
program, and provided by its caller. The same is true of the program ID of
the called program.
The Instruction
is usually built from within the calling program, but may
be deserialized from an external source.
This function will not return if the called program returns anything other
than success. If the callee returns an error or aborts then the entire
transaction will immediately fail. To return data as the result of a
cross-program invocation use the set_return_data
/ get_return_data
functions, or have the callee write to a dedicated account for that purpose.
A program may directly call itself recursively, but may not be indirectly called recursively (reentered) by another program. Indirect reentrancy will cause the transaction to immediately fail.
§Validation of shared data between programs
The AccountInfo
structures passed to this function contain data that is
directly accessed by the runtime and is copied to and from the memory space
of the called program. Some of that data, the AccountInfo::lamports
and
AccountInfo::data
fields, may be mutated as a side-effect of the called
program, if that program has writable access to the given account.
These two fields are stored in RefCell
s to enforce the aliasing
discipline for mutated values required by the Rust language. Prior to
invoking the runtime, this routine will test that each RefCell
is
borrowable as required by the callee and return an error if not.
The CPU cost of these runtime checks can be avoided with the unsafe
invoke_unchecked
function.
§Errors
If the called program completes successfully and violates no runtime
invariants, then this function will return successfully. If the callee
completes and returns a ProgramError
, then the transaction will
immediately fail. Control will not return to the caller.
Various runtime invariants are checked before the callee is invoked and before returning control to the caller. If any of these invariants are violated then the transaction will immediately fail. A non-exhaustive list of these invariants includes:
- The sum of lamports owned by all referenced accounts has not changed.
- A program has not debited lamports from an account it does not own.
- A program has not otherwise written to an account that it does not own.
- A program has not written to an account that is not writable.
- The size of account data has not exceeded applicable limits.
If the invoked program does not exist or is not executable then the transaction will immediately fail.
If any of the RefCell
s within the provided AccountInfo
s cannot be
borrowed in accordance with the call’s requirements, an error of
ProgramError::AccountBorrowFailed
is returned.
§Examples
A simple example of transferring lamports via CPI:
use solana_cpi::invoke;
use solana_account_info::{next_account_info, AccountInfo};
use solana_program_entrypoint::entrypoint;
use solana_program_error::ProgramResult;
use solana_pubkey::Pubkey;
use solana_program::{
system_instruction,
system_program,
};
entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let payer = next_account_info(account_info_iter)?;
let recipient = next_account_info(account_info_iter)?;
// The system program is a required account to invoke a system
// instruction, even though we don't use it directly.
let system_program_account = next_account_info(account_info_iter)?;
assert!(payer.is_writable);
assert!(payer.is_signer);
assert!(recipient.is_writable);
assert!(system_program::check_id(system_program_account.key));
let lamports = 1000000;
invoke(
&system_instruction::transfer(payer.key, recipient.key, lamports),
&[payer.clone(), recipient.clone(), system_program_account.clone()],
)
}