本文へスキップ
BecomeCoder

Solanaコース · 第6章 PDAとCPI · レッスン24

System Programでアカウントを作る ― invoke_signedで初期化する

ローカル実施

導入

これまでのレッスンでは「すでに存在するデータアカウント」を前提に読み書きしてきましたが、実際にはそのアカウント自体を最初に作る処理が必要です。アカウントの新規作成を担当するのは、Solana に最初から組み込まれている System Program です。ここでは PDA を CPI 経由で初期化する、実践的な流れをまとめて見ていきます。

説明

新しいアカウントを作る命令は system_instruction::create_account で組み立てます。

use solana_program::{
    account_info::AccountInfo,
    program::invoke_signed,
    pubkey::Pubkey,
    rent::Rent,
    sysvar::Sysvar,
    system_instruction,
    entrypoint::ProgramResult,
};

fn create_vault_pda(
    program_id: &Pubkey,
    payer: &AccountInfo,      // 作成コストを支払うアカウント(ユーザー)
    vault_pda: &AccountInfo,  // これから作るPDA本体
    system_program: &AccountInfo,
    bump: u8,
) -> ProgramResult {
    let space: u64 = 41; // VaultAccount(owner:32 + balance:8 + bump:1)のバイト数
    let rent = Rent::get()?;
    let lamports = rent.minimum_balance(space as usize); // レント免除に必要な最低残高

    let seeds: &[&[u8]] = &[b"vault", payer.key.as_ref(), &[bump]];

    invoke_signed(
        &system_instruction::create_account(
            payer.key,      // 支払い元
            vault_pda.key,  // 作成するアカウント(PDA)
            lamports,       // 入れておく初期SOL(レント免除分)
            space,          // 確保するデータ領域のバイト数
            program_id,     // このアカウントの所有者を自分のプログラムにする
        ),
        &[payer.clone(), vault_pda.clone(), system_program.clone()],
        &[seeds], // PDAの代理署名に使うseeds
    )?;

    Ok(())
}
flowchart TD
    A["ユーザーが初期化命令を送る<br/>(payer + vault PDA + system_program)"] --> B["find_program_addressでPDAとbumpを確認"]
    B --> C["Rent::get()でレント免除の必要残高を計算"]
    C --> D["system_instruction::create_accountを組み立て"]
    D --> E["invoke_signedでPDAの代理署名つきで実行"]
    E --> F["System Programがアカウントを作成<br/>ownerを自分のプログラムに設定"]
    F --> G["以後、このPDAにVaultAccountを読み書きできる"]

ポイントは、PDA 自身に秘密鍵がないため、PDA を作成する命令に対して「PDA からの署名」を用意できないということです。その代わりに、invoke_signed へ導出時と同じ seeds(と bump)を渡すことで、Solana ランタイムが「このプログラムはこの PDA を正しく導出できる=所有者として振る舞ってよい」と認め、代理署名として扱ってくれます。

ここまで見てきたとおり、ネイティブなプログラムでは「命令のデシリアライズ」「署名者・所有者チェック」「PDA の導出」「レント免除額の計算」「create_account の組み立て」といった定型的な手続きを、機能を作るたびに手で書く必要があります。この繰り返しの多さこそが、次章で学ぶ Anchor フレームワークが解決しようとしている課題です。Anchor では、こうした定型コードの大部分をマクロと属性(アトリビュート)が肩代わりしてくれます。

演習

この章で出てきた「PDAの作成」の一連の手順(find_program_address → レント免除額の計算 → create_account → invoke_signed)を、自分の言葉で1つずつ箇条書きにまとめてみましょう。次章で Anchor がどの手順を自動化してくれるか、比較しながら読み進める準備になります。

ヒント1を見る

手順は「アドレスを決める」「必要なSOLを計算する」「アカウント作成命令を組み立てる」「代理署名つきで実行する」の4段階に分けられます。

ヒント2を見る

このうちどれか1つでも欠けると、アカウントが作れない・作れても壊れた状態になる、とイメージすると理解が深まります。