概要
PDAのseedを使用して、_rsinvoke_signed_経由でPDAアカウントを作成します。所有プログラムのみがPDAに署名できます。Anchorの_rsinit_制約は、PDAアカウント作成を自動化します。
invoke_signedによるPDA署名
プログラムがCPI中にPDAの代わりに署名する必要がある場合、PDAのseedを使用して*rsinvoke_signed*を使用します。ランタイムは、呼び出し元プログラムのIDを使用してseedが期待されるPDAを導出することを検証し、所有プログラムのみが署名できることを保証します。完全な検証フローについては、PDA署名を参照してください。
PDAアカウントの作成
PDAの導出とPDAでのアカウント作成は別々の操作です。アドレスを導出した後、明示的にアカウントを作成する必要があります。
PDAでアカウントを作成するには、導出プログラムがinvoke_signed経由でSystem
Programの*rscreate_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の作成に使用されるseedsは次のとおりです。
- 固定文字列: "data"
- 命令で提供されたユーザーアカウントのアドレス
- 正規のbump seed
この例では、bump制約に値が割り当てられていないため、AnchorはPDAを導出してbumpを見つけるために*rsfind_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?