Samenvatting
Gebruik invoke_signed wanneer het aanroepende programma namens een PDA
die het bezit moet ondertekenen. De runtime leidt PDA-pubkeys af van de
verstrekte ondertekeningsseeds en voegt ze toe aan de set geldige
ondertekenaars voordat de rechtencontrole plaatsvindt.
CPI's met PDA-ondertekenaars
Wanneer een CPI een PDA-ondertekenaar vereist, gebruik
invoke_signed
met de ondertekeningsseeds die gebruikt worden om de
PDA af te leiden. Voor details over hoe de runtime
PDA-handtekeningen verifieert, zie
PDA-ondertekening.
pub fn invoke_signed(instruction: &Instruction,account_infos: &[AccountInfo],signers_seeds: &[&[&[u8]]],) -> ProgramResult {// --snip--invoke_signed_unchecked(instruction, account_infos, signers_seeds)}
De onderstaande voorbeelden maken een CPI met PDA-ondertekenaars met behulp van Anchor en Native Rust. Elk voorbeeld bevat een enkele instructie om SOL over te maken van een PDA naar een ontvangeraccount, met behulp van een CPI ondertekend door de PDA.
Anchor
De volgende voorbeelden tonen twee benaderingen voor het implementeren van CPI's in een Anchor-programma. De voorbeelden zijn functioneel equivalent, maar demonstreren verschillende abstractieniveaus.
- Voorbeeld 1: Gebruikt Anchor's
CpiContexten helperfunctie. - Voorbeeld 2: Gebruikt de
system_instruction::transfer-functie uitsolana_program-crate. (Voorbeeld 1 is een abstractie van deze implementatie.) - Voorbeeld 3: Construeert de CPI-instructie handmatig. Deze benadering is nuttig wanneer er geen crate beschikbaar is om de instructie te bouwen die je wilt aanroepen.
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
Het onderstaande voorbeeld maakt een CPI met PDA-ondertekenaars vanuit een programma geschreven in Native Rust. Het bevat een enkele instructie die SOL overdraagt van een PDA-account naar een ander. De CPI wordt ondertekend door het PDA-account. (Het testbestand gebruikt LiteSVM om het programma te testen.)
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(())}}}
Is this page helpful?