Cross Program Invocation
Bir cross-program invocation (CPI), bir Solana programının doğrudan başka bir programın talimatlarını çağırdığında gerçekleşir. Bu, program birleştirilebilirliğine olanak tanır. Bir Solana talimatını ağa açılan bir API uç noktası olarak düşünürseniz, CPI bir uç noktanın dahili olarak başka bir uç noktayı çağırması gibidir.
Bir CPI yaparken, bir program kendi program kimliğinden türetilen bir PDA adına imzalayabilir. Bu imzalayıcı ayrıcalıkları, çağıran programdan çağrılan programa kadar uzanır.
Cross-program invocation örneği
Bir CPI yapıldığında, hesap ayrıcalıkları bir programdan diğerine aktarılır. Diyelim ki Program A, bir imzalayıcı hesap ve yazılabilir bir hesap içeren bir talimat alıyor. Program A daha sonra Program B'ye bir CPI yapıyor. Şimdi, Program B, Program A ile aynı hesapları orijinal izinleriyle kullanabilir. (Yani Program B, imzalayıcı hesapla imzalayabilir ve yazılabilir hesaba yazabilir.) Program B kendi CPI'larını yaparsa, aynı izinleri 4 derinliğe kadar ileriye aktarabilir.
Program talimatı çağrısının maksimum yüksekliği max_instruction_stack_depth olarak adlandırılır ve MAX_INSTRUCTION_STACK_DEPTH sabitine 5 olarak ayarlanmıştır.
Yığın yüksekliği, başlangıç işlemi için 1'den başlar ve bir program başka bir talimatı çağırdığında 1 artar, CPI'lar için çağrı derinliğini 4 ile sınırlar.
PDA imzalayıcıları ile CPI'lar
Bir CPI, PDA imzalayanı gerektirdiğinde,
invoke_signed
fonksiyonu kullanılır. Bu fonksiyon, imzalayan PDA'ları
türetmek için kullanılan imzalayan seed'lerini alır.
Solana çalışma zamanı, dahili olarak çağıran programın signers_seeds ve
program_id kullanarak
create_program_address
fonksiyonunu çağırır. Bir PDA doğrulandığında,
geçerli bir imzalayan olarak eklenir.
pub fn invoke_signed(instruction: &Instruction,account_infos: &[AccountInfo],signers_seeds: &[&[&[u8]]],) -> ProgramResult {// --snip--invoke_signed_unchecked(instruction, account_infos, signers_seeds)}
Aşağıdaki örnekler, Anchor ve Native Rust kullanarak PDA imzalayanları ile CPI yapmayı göstermektedir. Her örnek, PDA tarafından imzalanmış bir CPI kullanarak SOL'u bir PDA'dan alıcı hesaba aktaran tek bir talimat içerir.
Anchor
Aşağıdaki örnekler, bir Anchor programında CPI'ları uygulamanın üç farklı yaklaşımını göstermektedir. Örnekler işlevsel olarak eşdeğerdir, ancak her biri farklı bir soyutlama düzeyi gösterir.
- Örnek 1: Anchor'ın 
CpiContextve yardımcı fonksiyonunu kullanır. - Örnek 2: 
solana_programcrate'indensystem_instruction::transferfonksiyonunu kullanır. (Örnek 1, bu uygulamanın bir soyutlamasıdır.) - Örnek 3: CPI talimatını manuel olarak oluşturur. Bu yaklaşım, çağırmak istediğiniz talimatı oluşturmaya yardımcı olacak bir crate mevcut olmadığında kullanışlıdır.
 
use anchor_lang::prelude::*;use anchor_lang::system_program::{transfer, Transfer};declare_id!("BrcdB9sV7z9DvF9rDHG263HUxXgJM3iCQdF36TcxbFEn");#[program]pub mod cpi {use super::*;pub fn sol_transfer(ctx: Context<SolTransfer>, amount: u64) -> Result<()> {let from_pubkey = ctx.accounts.pda_account.to_account_info();let to_pubkey = ctx.accounts.recipient.to_account_info();let program_id = ctx.accounts.system_program.to_account_info();let seed = to_pubkey.key();let bump_seed = ctx.bumps.pda_account;let signer_seeds: &[&[&[u8]]] = &[&[b"pda", seed.as_ref(), &[bump_seed]]];let cpi_context = CpiContext::new(program_id,Transfer {from: from_pubkey,to: to_pubkey,},).with_signer(signer_seeds);transfer(cpi_context, amount)?;Ok(())}}#[derive(Accounts)]pub struct SolTransfer<'info> {#[account(mut,seeds = [b"pda", recipient.key().as_ref()],bump,)]pda_account: SystemAccount<'info>,#[account(mut)]recipient: SystemAccount<'info>,system_program: Program<'info, System>,}
Rust
Aşağıdaki örnek, Native Rust ile yazılmış bir programdan PDA imzalayanları ile CPI yapmayı göstermektedir. Program, PDA hesabından başka bir hesaba SOL aktaran tek bir talimat içerir. CPI, PDA hesabı tarafından imzalanır. (Test dosyası, programı test etmek için LiteSVM kullanır.)
use borsh::BorshDeserialize;use solana_program::{account_info::AccountInfo,entrypoint,entrypoint::ProgramResult,program::invoke_signed,program_error::ProgramError,pubkey::Pubkey,system_instruction,};// Declare program entrypointentrypoint!(process_instruction);// Define program instructions#[derive(BorshDeserialize)]enum ProgramInstruction {SolTransfer { amount: u64 },}impl ProgramInstruction {fn unpack(input: &[u8]) -> Result<Self, ProgramError> {Self::try_from_slice(input).map_err(|_| ProgramError::InvalidInstructionData)}}pub fn process_instruction(program_id: &Pubkey,accounts: &[AccountInfo],instruction_data: &[u8],) -> ProgramResult {// Deserialize instruction datalet instruction = ProgramInstruction::unpack(instruction_data)?;// Process instructionmatch instruction {ProgramInstruction::SolTransfer { amount } => {// Parse accountslet [pda_account_info, recipient_info, system_program_info] = accounts else {return Err(ProgramError::NotEnoughAccountKeys);};// Derive PDA and verify it matches the account provided by clientlet recipient_pubkey = recipient_info.key;let seeds = &[b"pda", recipient_pubkey.as_ref()];let (expected_pda, bump_seed) = Pubkey::find_program_address(seeds, program_id);if expected_pda != *pda_account_info.key {return Err(ProgramError::InvalidArgument);}// Create the transfer instructionlet transfer_ix = system_instruction::transfer(pda_account_info.key,recipient_info.key,amount,);// Create signer seeds for PDAlet signer_seeds: &[&[&[u8]]] = &[&[b"pda", recipient_pubkey.as_ref(), &[bump_seed]]];// Invoke the transfer instruction with PDA as signerinvoke_signed(&transfer_ix,&[pda_account_info.clone(),recipient_info.clone(),system_program_info.clone(),],signer_seeds,)?;Ok(())}}}
PDA imzalayanları olmadan CPI'lar
Bir CPI, PDA imzalayanları gerektirmediğinde,
invoke
fonksiyonu kullanılır. invoke fonksiyonu, boş bir signers_seeds dizisi ile
invoke_signed fonksiyonunu çağırır. Boş imzalayanlar dizisi, imzalama için
hiçbir PDA'nın gerekmediğini belirtir.
pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {invoke_signed(instruction, account_infos, &[])}
Aşağıdaki örnekler Anchor ve Native Rust kullanarak CPI yapmayı göstermektedir. Her örnek, bir hesaptan diğerine SOL aktaran tek bir talimat içerir.
Anchor
Aşağıdaki örnekler, bir Anchor programında CPI'ları uygulamanın üç farklı yaklaşımını göstermektedir. Örnekler işlevsel olarak eşdeğerdir, ancak her biri farklı bir soyutlama düzeyi gösterir.
- Örnek 1: Anchor'ın 
CpiContextve yardımcı fonksiyonunu kullanır. - Örnek 2: 
solana_programcrate'indensystem_instruction::transferfonksiyonunu kullanır. - Örnek 3: CPI talimatını manuel olarak oluşturur. Bu yaklaşım, çağırmak istediğiniz talimatı oluşturmaya yardımcı olacak bir crate mevcut olmadığında kullanışlıdır.
 
use anchor_lang::prelude::*;use anchor_lang::system_program::{transfer, Transfer};declare_id!("9AvUNHjxscdkiKQ8tUn12QCMXtcnbR9BVGq3ULNzFMRi");#[program]pub mod cpi {use super::*;pub fn sol_transfer(ctx: Context<SolTransfer>, amount: u64) -> Result<()> {let from_pubkey = ctx.accounts.sender.to_account_info();let to_pubkey = ctx.accounts.recipient.to_account_info();let program_id = ctx.accounts.system_program.to_account_info();let cpi_context = CpiContext::new(program_id,Transfer {from: from_pubkey,to: to_pubkey,},);transfer(cpi_context, amount)?;Ok(())}}#[derive(Accounts)]pub struct SolTransfer<'info> {#[account(mut)]sender: Signer<'info>,#[account(mut)]recipient: SystemAccount<'info>,system_program: Program<'info, System>,}
Rust
Aşağıdaki örnek, Native Rust ile yazılmış bir programdan nasıl CPI yapılacağını göstermektedir. Örnek, bir hesaptan diğerine SOL aktaran tek bir talimat içerir. (Test dosyası, programı test etmek için LiteSVM kullanır.)
use borsh::BorshDeserialize;use solana_program::{account_info::AccountInfo,entrypoint,entrypoint::ProgramResult,program::invoke,program_error::ProgramError,pubkey::Pubkey,system_instruction,};// Declare program entrypointentrypoint!(process_instruction);// Define program instructions#[derive(BorshDeserialize)]enum ProgramInstruction {SolTransfer { amount: u64 },}impl ProgramInstruction {fn unpack(input: &[u8]) -> Result<Self, ProgramError> {Self::try_from_slice(input).map_err(|_| ProgramError::InvalidInstructionData)}}pub fn process_instruction(_program_id: &Pubkey,accounts: &[AccountInfo],instruction_data: &[u8],) -> ProgramResult {// Deserialize instruction datalet instruction = ProgramInstruction::unpack(instruction_data)?;// Process instructionmatch instruction {ProgramInstruction::SolTransfer { amount } => {// Parse accountslet [sender_info, recipient_info, system_program_info] = accounts else {return Err(ProgramError::NotEnoughAccountKeys);};// Verify the sender is a signerif !sender_info.is_signer {return Err(ProgramError::MissingRequiredSignature);}// Create and invoke the transfer instructionlet transfer_ix = system_instruction::transfer(sender_info.key,recipient_info.key,amount,);invoke(&transfer_ix,&[sender_info.clone(),recipient_info.clone(),system_program_info.clone(),],)?;Ok(())}}}
Is this page helpful?