요약
호출 프로그램이 소유한 PDA를 대신하여 서명해야 할 때 _rsinvoke_signed_를
사용하세요. 런타임은 제공된 서명자 시드에서 PDA 공개키를 파생하고 권한 확인
전에 유효한 서명자 집합에 추가합니다.
PDA 서명자를 사용한 CPI
CPI에 PDA 서명자가 필요한 경우, PDA를 파생하는 데 사용된
signer seeds와 함께
invoke_signed를
사용하세요. 런타임이 PDA 서명을 검증하는 방법에 대한 자세한 내용은
PDA 서명을 참조하세요.
Invoke signed
pub fn invoke_signed(instruction: &Instruction,account_infos: &[AccountInfo],signers_seeds: &[&[&[u8]]],) -> ProgramResult {// --snip--invoke_signed_unchecked(instruction, account_infos, signers_seeds)}
아래 예제는 Anchor 및 네이티브 Rust를 사용하여 PDA 서명자로 CPI를 수행합니다. 각 예제에는 PDA가 서명한 CPI를 사용하여 PDA에서 수신자 계정으로 SOL을 전송하는 단일 명령이 포함되어 있습니다.
Anchor
다음 예제는 Anchor 프로그램에서 CPI를 구현하는 두 가지 접근 방식을 보여줍니다. 예제는 기능적으로 동일하지만 서로 다른 수준의 추상화를 보여줍니다.
- 예제 1: Anchor의
CpiContext및 헬퍼 함수를 사용합니다. - 예제 2:
solana_program크레이트의system_instruction::transfer함수를 사용합니다. (예제 1은 이 구현의 추상화입니다.) - 예제 3: CPI 명령을 수동으로 구성합니다. 이 접근 방식은 호출하려는 명령을 빌드하는 데 도움이 되는 크레이트가 없을 때 유용합니다.
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
아래 예제는 네이티브 Rust로 작성된 프로그램에서 PDA 서명자를 사용한 CPI를 수행합니다. SOL을 PDA 계정에서 다른 계정으로 전송하는 단일 명령어를 포함합니다. CPI는 PDA 계정에 의해 서명됩니다. (테스트 파일은 프로그램을 테스트하기 위해 LiteSVM을 사용합니다.)
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?