一般
ペイヤー・オーソリティ パターン
P-Aパターンの概要
ペイヤー・オーソリティ(P-A)パターンは、ストレージまたはレントの支払いを行う当事者(ペイヤー)と、アカウントを所有または制御する当事者(オーソリティ)が異なる場合があるシナリオにおいて、Solanaプログラム命令を構造化する一般的なアプローチです。これは最大限の構成可能性を持つプロトコルを設計する際の強力なデフォルト動作として機能し、Metaplexプログラムライブラリの基盤となっています。
これらの役割を分離することで、プログラムはより柔軟な資金調達メカニズム(1つまたは複数のペイヤー)とより明確な所有権または制御セマンティクスに対応できます。例えば、ゲームにおいて、ユーザーにアカウント初期化の支払いをさせたいが、その後のアクションではプログラムまたはPDAがオーソリティとして機能させたい場合があります。
異なる2つの署名者が必要な理由
異なる責任:
責任を分割することで、一方の署名者がアカウント作成やレントの支払いを行い、もう一方の署名者がそのアカウントを実際に管理または所有することができます。これは特に大規模または複雑なプログラムにおいて重要な懸念の分離です。柔軟性:
トランザクションに資金を提供する当事者が、最終的にアカウントを制御する当事者と同じでない場合があります。2つの役割を設定することで、スポンサーがオンチェーンストレージの代金を支払いながら、エンドユーザーがアセットの自律性と所有権を保持するパターンに簡単に対応できます。PDA署名者: プログラム派生アドレス(PDA)は、通常のキーペアと同じ方法でトランザクションに署名することを可能にする秘密鍵を持たないため、それらのすべての相互作用はプログラムを呼び出すことによって管理される必要があります。PDAはアカウントのオーソリティになることができますが、複雑な資金移動を伴わずにレントや手数料を直接支払うために使用することはできません。PDAの代わりにレントや小さなストレージ調整をカバーする別のペイヤーアカウントを持つことで、軽微な変更のために資金をPDAに誘導する複雑さを回避できます。
Rustの例
以下は、ShankとAnchorの両方でP-Aパターンを実装する方法の例です。また、これらの署名者条件を検証する方法と、このパターンで動作するクライアントを構築する方法についても説明します。
Rustにおけるペイヤーオーソリティパターン
/// 新しいアカウントを作成します。
#[account(0, writable, signer, name="account", desc = "新しいアカウントのアドレス")]
#[account(1, writable, signer, name="payer", desc = "ストレージ料金を支払うアカウント")]
#[account(2, optional, signer, name="authority", desc = "アカウント作成に署名するオーソリティ")]
#[account(3, name="system_program", desc = "システムプログラム")]
CreateAccountV1(CreateAccountV1Args),
制約チェック
ネイティブなSolanaコードでは、各命令に対して正しい署名者が存在することを確認する必要があります。これは通常、以下を意味します:
// ペイヤーがトランザクションに署名し、ストレージ料金の支払いに同意していることを確認します。
assert_signer(ctx.accounts.payer)?;
// オーソリティが存在する場合、それらが署名者であることを確認します。そうでなければ、
// ペイヤーをトランザクションを承認する者として扱います。
let authority = match ctx.accounts.authority {
Some(authority) => {
assert_signer(authority)?;
authority
}
None => ctx.accounts.payer,
};
重要なポイント
assert_signerは、提供されたアカウントキーがトランザクションに署名していることを確認します。フォールバックロジックを設定します:オーソリティが提供されていない場合、ペイヤーをオーソリティとして扱います。 これは事実上P-Aパターンの本質を捉えています:別々のオプションのオーソリティがアカウント作成や変更を管理できますが、オーソリティが提供されていない場合、ペイヤーがデフォルトでその役割を担います。
クライアントの見た目
クライアント側から、トランザクションにペイヤーとオーソリティ(オプション)の両方を渡す必要があります。以下は、CreateAccountV1命令でこれらのアカウントがどのように構造化されるかを示すUmiを使用した例です。
ペイヤーオーソリティパターン クライアント
// アカウント。
export type CreateAccountV1InstructionAccounts = {
/** 新しいアカウントのアドレス */
account: Signer;
/** ストレージ料金を支払うアカウント */
payer: Signer;
/** 新しいアセットのオーソリティ */
authority?: Signer | Pda;
/** システムプログラム */
systemProgram?: PublicKey | Pda;
};
まとめ
ペイヤー・オーソリティパターンは、アカウントの資金提供者(ペイヤー)がアカウントの所有者または管理者(オーソリティ)と異なる状況を処理するエレガントな方法です。別々の署名者を要求し、オンチェーンロジックでそれらを検証することで、プログラムで明確で堅牢で柔軟な所有権セマンティクスを維持できます。Rust(ShankとAnchor)のサンプルコードとUmiクライアントの例は、このパターンをエンドツーエンドで実装する方法を示しています。
アカウント作成やトランザクション料金を支払うエンティティとは異なる可能性がある専用のアカウントオーソリティが必要になることが予想される場合、またはユーザーがプログラムにCPIすることが期待される状況では、このパターンを使用してください。これにより、コアプログラムロジックを複雑にすることなく、より洗練されたシナリオを簡単に処理できるようになります。
