本文へスキップ
BecomeCoder

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

PDA(Program Derived Address) ― プログラムが持つアカウント

ローカル実施

導入

通常のウォレットのアドレスには、必ずペアになる秘密鍵が存在します。ところが Solana には、秘密鍵がまったく存在しないのに有効なアドレスとして扱われる、少し不思議なアカウントがあります。これが **PDA(Program Derived Address / プログラム派生アドレス)**です。

説明

通常の Pubkey(公開鍵)は、ランダムな秘密鍵から数学的に計算されるため、必ず「ed25519 曲線」という曲線の上に乗ります。一方 PDA は、プログラムのアドレス(program_id)といくつかの「シード(seeds)」という材料から意図的に計算され、曲線から外れた場所に落ちるように作られたアドレスです。曲線上にないアドレスには対応する秘密鍵が存在しえないため、誰も個人の秘密鍵で署名できません。その代わり、生成元のプログラムだけが「代理署名(次のレッスンで学ぶ invoke_signed)」できる、という特別な性質を持ちます。

use solana_program::pubkey::Pubkey;

// seeds(材料)とprogram_idから、PDAのアドレスとbumpを求める
let (pda, bump) = Pubkey::find_program_address(
    &[b"vault", user.key.as_ref()],
    program_id,
);
flowchart LR
    S1["シード1: b\"vault\"<br/>(固定の目印文字列)"] --> F["find_program_address"]
    S2["シード2: user.key<br/>(ユーザーごとに変わる)"] --> F
    P["program_id"] --> F
    F --> PDA["PDAアドレス<br/>(曲線外・秘密鍵なし)"]
    F --> BUMP["bump(0〜255の調整値)"]

find_program_address は、指定したシードから計算したアドレスが「たまたま曲線に乗ってしまう」場合に備えて、末尾に付け足す bump(バンプ) という1バイトの値を 255 から順に試し、曲線から外れる最初の組み合わせを見つけて返します。同じシード・同じプログラムであれば、何度計算しても必ず同じ PDA と同じ bump が求まるという決定性(deterministic)が、この仕組みの核心です。

分かりやすい例として、「ユーザーごとに1つずつ、保管庫(vault)のアカウントを持たせたい」というケースを考えてみましょう。["vault", ユーザーのアドレス] というシードを使えば、ユーザーのアドレスさえ分かれば、誰でもいつでも同じ保管庫アドレスを計算できます。しかもそのアドレスには秘密鍵がないので、ユーザー本人はもちろん誰も直接それを操作できず、プログラムのロジックだけがその中身を動かせる――という安全な設計になります。

演習

「PDA には秘密鍵が存在しない」という性質が、なぜ「プログラムだけがそのアカウントを操作できる」という安全性につながるのか、自分の言葉で説明してみましょう。

ヒント1を見る

通常のアカウントは秘密鍵を持つ人が署名すれば誰でも操作できます。PDAにはその「持ち主」がいません。

ヒント2を見る

秘密鍵で署名できない代わりに、プログラムが特別な方法で「代理署名」する仕組みが次のレッスンで出てきます。