見出し画像

【Solanaコアコンセプト⑤】 プログラム派生アドレス

この記事は、こちらの公式を翻訳・編集したものです。

1 はじめに

Program Derived Address(PDA)について説明します。

PDAはSolanaの開発者に以下の2つの主な用途を提供します。

1 決定論的アカウントアドレス

PDAは、オプショナルな「シード」(事前に定義された入力)特定のプログラムIDの組み合わせを使用します。

これにより、アドレスを決定論的に導出するメカニズムを提供します。

補足
「決定論的」とは同じ値を入れれば、必ず同じ値になるようなことです。

2 プログラムによる署名可能性

Solanaのランタイムは、そのプログラムIDから導出されたPDAに対してプログラムが「署名」することを可能にします。

3 PDAの特徴

PDAは、事前に定義された入力セット(例えば、文字列、数字、その他のアカウントアドレス)からオンチェーンでハッシュマップのような構造を作成する方法と考えることができます。

補足
つまり、キーと値の関係性で管理できます。

https://www.boardinfinity.com/blog/hashmap/

このアプローチの利点は、正確なアドレスを追跡する必要がなくなることです。

代わりに、その導出に使用された特定の入力を思い出すだけで済みます。

補足
この例では、ウォレットの公開鍵をシードとしています。
これにより、PDAのアドレスその公開鍵から導くことができます。

4 PDAの注意点(なぜ略称はアカウントではない?)

重要なのは、Program Derived Address(PDA)を導出しても、そのアドレスに自動的にオンチェーンアカウントが作成されるわけではないということです。

PDAをオンチェーンアドレスとして持つアカウントは、そのアドレスを導出するために使用されたプログラムを通じて明示的に作成する必要があります。

補足
ポイントとなるアドレスを見つけただけで、アカウントが作られているわけではないのですね。

PDAの導出は、地図上のアドレスを見つけることと考えることができます。

アドレスがあるからといって、その場所に何かが建設されているわけではありません。

2 キーポイント

1 決定論的

PDAは、ユーザー定義のシードバンプシードプログラムのIDの組み合わせを使用して決定論的に導出されるアドレスです。

2 秘密鍵の存在

PDAはEd25519曲線から外れたアドレスであり、対応する秘密鍵は存在しません。

3 プログラムによる署名

Solanaプログラムは、そのプログラムIDを使用して導出されたPDAに対してプログラム的に「署名」することができます。

4 アカウント作成のタイミング

PDAを導出しても自動的にオンチェーンアカウントが作成されるわけではありません。

5 作成方法

PDAをアドレスとして使用するアカウントは、Solanaプログラム内の専用命令(Instruction)を通じて明示的に作成する必要があります。

3 PDAとは何か

 1 秘密鍵の存在

PDAは決定論的に導出されるアドレスで、標準的な公開鍵のように見えますが、関連する秘密鍵はありません。

2 プログラムによる署名

これは、外部のユーザーがそのアドレスに対して有効な署名を生成することができないことを意味します。

ただし、Solanaのランタイムは、秘密鍵を必要とせずにプログラムがPDAに対してプログラム的に「署名」することを可能にします。

3 Ed25519曲線(楕円曲線暗号)上の点

Solanaのキーペアは、Ed25519曲線(楕円曲線暗号)上の点で、公開鍵と対応する秘密鍵を持っています。

通常、公開鍵を新しいオンチェーンアカウントのユニークIDとして使用し、秘密鍵で署名します。

4 Ed25519曲線(楕円曲線暗号)外の点

PDAは、事前に定義された入力セットを使用して、意図的にEd25519曲線から外れるように導出される点です。

Ed25519曲線上にない点は、有効な対応する秘密鍵を持たず、暗号操作(署名)には使用できません。

その後、PDAはオンチェーンアカウントのアドレス(固有の識別子)として使用することができます。

プログラムの状態を簡単に保存、マッピング、および取得する方法を提供します。

4 PDAの導出方法

PDAの導出方法には3つの入力が必要です。

1 オプショナルシード

PDAを導出するために使用される事前定義された入力(例:文字列、数値、他のアカウントアドレス)です。

これらの入力はバイトのバッファに変換されます。

2 バンプシード

有効なPDA(曲線から外れたもの)が生成されることを保証するために使用される追加の入力(値は255から0の間)です。

このバンプシード(255から始まる)は、PDAを生成する際にオプショナルシードに追加され、Ed25519曲線からポイントを「バンプ」します。

バンプシードは時々「ノンス」とも呼ばれます。

3 プログラムID

PDAが導出されるプログラムのアドレスです。

これはまた、PDAを代わって「署名」できるプログラムでもあります。

4 PDAの取得(FindProgramAddressSync)

PDAを導出するために、@solana/web3.js から findProgramAddressSync メソッドを使用できます。

この関数の同等物は他のプログラミング言語(例:Rust)にもありますが、このセクションではJavaScriptを使用した例を説明します。

findProgramAddressSync メソッドを使用する際には、以下を渡します。

①バイトのバッファに変換された事前定義のオプショナルシード

②PDAを導出するために使用されるプログラムID(アドレス)

https://github.com/solana-labs/solana-web3.js/blob/ca9da583a39cdf8fd874a2e03fccdc849e29de34/packages/library-legacy/src/publickey.ts#L212

有効なPDAが見つかると、findProgramAddressSyncは導出されたPDAバンプシードの両方を返します。

https://github.com/solana-labs/solana-web3.js/blob/ca9da583a39cdf8fd874a2e03fccdc849e29de34/packages/library-legacy/src/publickey.ts#L212

5 取得の例① 空白の場合

以下の最初の例では、オプショナルシードを提供せずにPDAを導出します。

Solana Playgroundでこの例を実行できます。

PDAバンプシードの出力は常に同じです。

https://beta.solpg.io/66031e5acffcf4b13384cfef

6 取得の例② 値を渡す場合

次の例ではオプショナルシード "helloWorld" を追加します。

この例もSolana Playgroundで実行できます。

https://beta.solpg.io/66031ee5cffcf4b13384cff0

PDAバンプシードの出力常に同じです。

バンプシードが254であることに注意してください。

これは255がEd25519曲線上のポイントを導出し、有効なPDAではないことを意味します。

findProgramAddressSyncによって返されるバンプシードは、与えられたオプショナルシードとプログラムIDの組み合わせで最初に有効なPDAを導出する値(255-0の間)です。

https://github.com/solana-labs/solana-web3.js/blob/ca9da583a39cdf8fd874a2e03fccdc849e29de34/packages/library-legacy/src/publickey.ts#L212

7 使用するバンプについて

最初に有効なバンプシードは「正統なバンプ」と呼ばれます。

プログラムのセキュリティのため、PDAを扱う際には標準的なバンプのみを使用することが推奨されます。

補足
確かに、該当するようなバンプはたくさん見つけられると思いますが、この「正統なバンプ」を使わないとどれを使えばいいかわからなくなります。

8 CreateProgramAddress

内部的には、findProgramAddressSyncはシードバッファに追加のバンプシード(ノンス)を反復的に追加します。

https://github.com/solana-labs/solana-web3.js/blob/ca9da583a39cdf8fd874a2e03fccdc849e29de34/packages/library-legacy/src/publickey.ts#L212

そして、createProgramAddressSyncメソッドを呼び出します。

バンプシードは255から始まり、有効なPDA(曲線から外れたもの)が見つかるまで1ずつ減少させます。

前述の例を再現するには、createProgramAddressSyncを使用して、明示的にバンプシード254を渡すことができます。

https://beta.solpg.io/66031f8ecffcf4b13384cff1

同じシードとプログラムIDを与えた場合、PDAの出力は前のものと一致します

9 正統なバンプ

「正統なバンプ」とは、有効なPDAを導出するために255から1ずつ減少させた最初のバンプシードを指します。

プログラムのセキュリティのため、正統なバンプから導出されたPDAのみを使用することが推奨されます。

前の例を参考にして、以下の例では255から0までのすべてのバンプシードを使用してPDAを導出しようと試みます。

https://beta.solpg.io/66032009cffcf4b13384cff2

予想通り、バンプシード255はエラーを発生させ、有効なPDAを導出する最初のバンプシードは254です。

しかし、バンプシード253から251までも有効なPDA異なるアドレスで導出することに注意してください。

これは、同じオプショナルシードとprogramIdを与えた場合、異なる値のバンプシードでも有効なPDAを導出できることを意味します。

10 正統なバンプの確認について(重要)

Solanaプログラムを構築する際には、プログラムに渡されたPDAが正統なバンプを使用して導出されたものであることを確認するセキュリティチェックを含めることを推奨します。

これを怠ると、プログラムに予期しないアカウントが提供される脆弱性が発生する可能性があります。

5 PDAアカウントの作成

1 サンプルプログラムについて

このサンプルプログラムは、新しいアカウントのアドレスとしてPDAを使用してアカウントを作成する方法を示しています。

https://beta.solpg.io/github.com/ZYJLiu/doc-examples/tree/main/pda-account

このサンプルプログラムはAnchorフレームワークを使用して書かれています。

2 PDAのアカウントの作成

lib.rsファイルには、アカウントのアドレスとしてPDAを使用して新しいアカウントを作成する単一の命令を含むプログラムがあります。

新しいアカウントは、ユーザーのアドレスとPDAを導出するために使用されたバンプシード格納します。

3 シードの指定

PDAを導出するために使用されるシードには、ハードコードされた文字列データと命令で提供されたユーザーアカウントのアドレスが含まれています。

Anchorフレームワークは自動的正統なバンプシードを導出します。

4 init制約について

init制約は、PDAをアドレスとして使用して新しいアカウントを作成するためにシステムプログラムを呼び出すようAnchorに指示します。

これはCPIを通じて内部的に行われます。

補足
CPIは次の記事で扱います。

5 JavaScriptでの同様のコードについて

テストファイル(pda-account.test.ts)で、PDAを導出するためのJavascript相当のコードを見ることができます。

https://beta.solpg.io/github.com/ZYJLiu/doc-examples/tree/main/pda-account

6 トランザクションの送信

その後、PDAをアドレスとして使用して新しいオンチェーンアカウントを作成するinitialize命令を呼び出すためのトランザクションが送信されます。

7 PDAのアカウントの取得

トランザクションが送信されると、そのアドレスで作成されたオンチェーンアカウントを取得するためにPDAが使用されます。

同じユーザーアドレスをシードとしてinitialize命令を複数回呼び出すと、トランザクションは失敗します。

これは、導出されたアドレスに既にアカウントが存在するためです。

サポートをしていただけたらすごく嬉しいです😄 いただけたサポートを励みに、これからもコツコツ頑張っていきます😊