요약
PDA의 seed를 사용하여 _rsinvoke_signed_를 통해 PDA 계정을 생성합니다. 소유
프로그램만 PDA에 서명할 수 있습니다. Anchor의 init 제약 조건은 PDA 계정
생성을 자동화합니다.
invoke_signed를 통한 PDA 서명
프로그램이 CPI 중에 PDA를 대신하여 서명해야 할 때, PDA의 seed와 함께
*rsinvoke_signed*를 사용합니다. 런타임은 호출 프로그램의 ID를 사용하여
seed가 예상 PDA를 도출하는지 검증하며, 이를 통해 소유 프로그램만 서명할 수
있도록 보장합니다. 전체 검증 흐름은
PDA 서명을 참조하세요.
PDA 계정 생성
PDA 도출과 PDA에 계정 생성은 별도의 작업입니다. 주소를 도출한 후 명시적으로 계정을 생성해야 합니다.
PDA에 계정을 생성하려면, 도출 프로그램이 invoke_signed를
통해 System Program의 create_account 명령을 호출하고, 런타임이 해당 주소에
대한 프로그램의 권한을 검증할 수 있도록 PDA의 seed를 전달합니다.
아래 예제는 Anchor 프레임워크를 사용하여
프로그램 파생 주소로 새 계정을 생성합니다. 프로그램에는 새 계정을 생성하는 단일
initialize 명령이 포함되어 있으며, 이 계정은 PDA를
도출하는 데 사용된 사용자 주소와
bump seed를 저장합니다.
use anchor_lang::prelude::*;declare_id!("75GJVCJNhaukaa2vCCqhreY31gaphv7XTScBChmr1ueR");#[program]pub mod pda_account {use super::*;pub fn initialize(ctx: Context<Initialize>) -> Result<()> {let account_data = &mut ctx.accounts.pda_account;// store the address of the `user`account_data.user = *ctx.accounts.user.key;// store the canonical bumpaccount_data.bump = ctx.bumps.pda_account;Ok(())}}#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub user: Signer<'info>,#[account(init,// define the seeds to derive the PDAseeds = [b"data", user.key().as_ref()],// use the canonical bumpbump,payer = user,space = 8 + DataAccount::INIT_SPACE // 8 bytes for Anchor account discriminator)]pub pda_account: Account<'info, DataAccount>,pub system_program: Program<'info, System>,}#[account]#[derive(InitSpace)]pub struct DataAccount {pub user: Pubkey,pub bump: u8,}
init 제약 조건은 Anchor에게 PDA를 주소로 사용하여 새 계정을
생성하도록
System Program을 호출하라고
지시합니다. PDA를 생성하는 데 사용된 seed는 다음과 같습니다.
- 고정 문자열: "data"
- 명령어에 제공된 사용자 계정의 주소
- 표준 bump seed
이 예제에서는 bump 제약 조건에 값이 할당되지 않았으므로 Anchor는 PDA를 파생하고
bump를 찾기 위해 *find_program_address*를 사용합니다.
#[account(init,seeds = [b"data", user.key().as_ref()],bump,payer = user,space = 8 + DataAccount::INIT_SPACE // 8 bytes for Anchor account discriminator)]pub pda_account: Account<'info, DataAccount>,
아래 테스트 파일에는 프로그램 파생 주소로 새 계정을 생성하기 위해
initialize 명령어를 호출하는 트랜잭션이 포함되어
있습니다. 이 파일에는 PDA를 파생하는 코드가 포함되어 있습니다.
이 예제는 생성될 새 계정을 가져오는 방법도 보여줍니다.
import * as anchor from "@coral-xyz/anchor";import { Program } from "@coral-xyz/anchor";import { PdaAccount } from "../target/types/pda_account";import { PublicKey } from "@solana/web3.js";describe("pda-account", () => {const provider = anchor.AnchorProvider.env();anchor.setProvider(provider);const program = anchor.workspace.PdaAccount as Program<PdaAccount>;const user = provider.wallet as anchor.Wallet;// Derive the PDA address using the seeds specified on the programconst [PDA] = PublicKey.findProgramAddressSync([Buffer.from("data"), user.publicKey.toBuffer()],program.programId);it("Is initialized!", async () => {const transactionSignature = await program.methods.initialize().accounts({user: user.publicKey}).rpc();console.log("Transaction Signature:", transactionSignature);});it("Fetch Account", async () => {const pdaAccount = await program.account.dataAccount.fetch(PDA);console.log(JSON.stringify(pdaAccount, null, 2));});});
동일한 user 주소 seed로 initialize 명령어를 다시 호출하면 트랜잭션이
실패합니다. 이는 파생된 주소에 이미 계정이 존재하기 때문입니다.
Is this page helpful?