プログラム間の呼び出し
クロスプログラム呼び出し#
Solanaランタイムを使用すると、プログラム間呼び出しと呼ばれるメカニズムを介してプログラムが相互に呼び出すことができます。プログラム間の呼び出しは、一方のプログラムが他方の命令を呼び出すことによって実現されます。呼び出されたプログラムは、呼び出されたプログラムが命令の処理を終了するまで停止されます。
たとえば、クライアントは、それぞれが別々のオンチェーンプログラムによって所有されている2つのアカウントを変更するトランザクションを作成できます。
クライアントは、代わりに、acme
プログラムtoken
がクライアントに代わって命令を便利に呼び出すことを許可する場合があります。
2つのオンチェーンプログラムtoken
とacme
、それぞれの実装命令pay()
とが与えられた場合、 プログラム間呼び出しを発行することによりlaunch_missiles()
、token
モジュールで定義された関数の呼び出しでacmeを実装できます。
invoke()
Solanaのランタイムに組み込まれており、指定された命令をtoken
命令のprogram_id
フィールドを介してプログラムにルーティングする役割を果たします。
invoke
呼び出し元は、呼び出される命令に必要なすべてのアカウントを渡す必要があることに注意してください。これは、実行可能アカウント(命令のプログラムIDと一致するアカウント)と命令プロセッサに渡されるアカウントの両方を意味します。
を呼び出す前pay()
に、ランタイムはacme
、が所有するアカウントが変更されていないことを確認する必要がありますtoken
。これは、ランタイムのポリシーを、acme
呼び出し時のアカウントの現在の状態と、命令のinvoke
開始時のアカウントの初期状態に適用することによって行われますacme
。pay()
完了後 、ランタイムは、ランタイムのポリシーを再度適用することtoken
によりacme
、所有するアカウントが変更されていないことを再度確認する必要がありますが、今回はtoken
プログラムIDを使用します。最後に、pay_and_launch_missiles()
完了後、ランタイムはランタイムポリシーをもう一度適用する必要がありますpre_*
。通常は適用されますが、更新されたすべての変数を使用します。無効なアカウント変更pay_and_launch_missiles()
をpay()
行わないまで実行した場合 、 pay()
無効な変更を行わず、returnsが無効な変更を行わないpay()
まで 実行するとpay_and_launch_missiles()
、ランタイムはpay_and_launch_missiles()
、全体として無効なアカウントの変更が行われなかったと推移的に想定できるため、これらすべてのアカウントの変更をコミットできます。
特権を必要とする指示#
ランタイムは、呼び出し元プログラムに付与された特権を使用して、呼び出し先に拡張できる特権を決定します。このコンテキストでの特権は、署名者と書き込み可能なアカウントを指します。たとえば、呼び出し元が処理している命令に署名者または書き込み可能なアカウントが含まれている場合、呼び出し元はその署名者および/または書き込み可能なアカウントも含む命令を呼び出すことができます。
この特権拡張は、プログラムのアップグレードの特別な場合を除いて、プログラムが不変であるという事実に依存しています。
acme
プログラムの場合、ランタイムはトランザクションの署名をtoken
命令の署名として安全に扱うことができます。ランタイムは token
命令参照 を確認するとalice_pubkey
、acme
命令内のキーを検索して、そのキーが署名されたアカウントに対応しているかどうかを確認します。この場合、それはそれを行い、それによってtoken
プログラムがアリスのアカウントを変更することを許可します。
プログラム署名済みアカウント#
プログラムは、プログラムから派生したアドレスを使用して、元のトランザクションで署名されなかった署名済みアカウントを含む命令を発行できます。
プログラムから派生したアドレスでアカウントに署名するために、プログラムは invoke_signed()
。
深さを呼び出す#
プログラム間の呼び出しにより、プログラムは他のプログラムを直接呼び出すことができますが、現在、深さは4に制限されています。
リエントラント#
現在、リエントラントは、固定された深さで制限された直接自己再帰に制限されています。この制限により、プログラムが後でコールバックされる可能性があることを知らずに、プログラムが中間状態から別のプログラムを呼び出す可能性がある状況が防止されます。直接再帰により、プログラムはコールバックされた時点でその状態を完全に制御できます。
プログラム派生アドレス#
プログラムから派生したアドレスを使用すると、プログラム間で呼び出すときにプログラムで生成された署名を使用できます。
プログラムから派生したアドレスを使用して、プログラムにアカウントに対する権限を付与し、後でその権限を別のアカウントに転送することができます。これが可能なのは、プログラムが権限を与えるトランザクションの署名者として機能できるためです。
たとえば、2人のユーザーがソラナでのゲームの結果に賭けをしたい場合、それぞれのユーザーは賭けの資産を、合意を尊重する仲介者に譲渡する必要があります。現在、仲介プログラムは資産を勝者に譲渡できないため、この仲介をソラナのプログラムとして実装する方法はありません。
この機能は、新しい所有者を決定するイベントが発生するまでアセットをエスクローエージェントに転送する必要があるため、多くのDeFiアプリケーションに必要です。
- 一致するビッドオーダーとアスクオーダーの間で資産を転送する分散型取引所。
- 資産を勝者に譲渡するオークション。
- 賞品を集めて勝者に再配布するゲームまたは予測市場。
プログラムから派生したアドレス:
- プログラムがプログラムアドレスと呼ばれる特定のアドレスを制御できるようにして、外部ユーザーがそれらのアドレスの署名を使用して有効なトランザクションを生成できないようにします。
- クロスプログラム呼び出しを介して呼び出される命令に存在するプログラムアドレスにプログラムがプログラムで署名できるようにします。
2つの条件が与えられると、ユーザーはオンチェーンアセットの権限をプログラムアドレスに安全に転送または割り当てることができ、プログラムはその裁量でその権限を他の場所に割り当てることができます。
プログラムアドレスの秘密鍵#
プログラムアドレスはed25519曲線上にないため、有効な秘密鍵が関連付けられていないため、その署名を生成することはできません。独自の秘密鍵はありませんが、プログラムが署名者としてプログラムアドレスを含む命令を発行するために使用できます。
ハッシュベースで生成されたプログラムアドレス#
プログラムアドレスは、256ビットの原像耐性ハッシュ関数を使用してシードのコレクションとプログラムIDから決定論的に導出されます。秘密鍵が関連付けられていないことを確認するために、プログラムアドレスがed25519曲線上にないようにする必要があります。生成中に、アドレスが曲線上にあることが判明した場合、エラーが返されます。シードとプログラムIDの特定のコレクションに対して、これが発生する可能性は約50/50です。これが発生した場合は、別のシードセットまたはシードバンプ(追加の8ビットシード)を使用して、曲線から外れた有効なプログラムアドレスを見つけることができます。
プログラムの決定論的プログラムアドレスは、で作成されたアカウントと同様の派生パスに従い、SystemInstruction::CreateAccountWithSeed
で実装されPubkey::create_with_seed
ます。
参考までに、その実装は次のとおりです。
プログラムは、シードを使用して、任意の数のアドレスを決定論的に導出できます。これらのシードは、アドレスがどのように使用されているかを象徴的に識別できます。
からPubkey
::
プログラムアドレスの使用#
クライアントはこのcreate_program_address
関数を使用して宛先アドレスを生成できます。この例ではcreate_program_address(&[&["escrow"]], &escrow_program_id)
、が曲線から外れた有効なプログラムアドレスを生成すると仮定し ます。
プログラムは同じ関数を使用して同じアドレスを生成できます。以下の関数では、プログラムtoken_instruction::transfer
は、トランザクションに署名するための秘密鍵を持っているかのように、プログラムアドレスからを発行します。
を使用して生成されたアドレスcreate_program_address
は、カーブ外の有効なプログラムアドレスであるとは限らないことに注意してください。たとえば、シード"escrow2"
が有効なプログラムアドレスを生成しないと仮定しましょう。
"escrow2
シードとしてを使用して有効なプログラムアドレスを生成するには、を使用して find_program_address
、有効な組み合わせが見つかるまで、可能なバンプシードを反復処理します。前の例は次のようになります。
プログラム内では、これは次のようになります。
以来find_program_address
へのコール数を繰り返し処理が必要です create_program_address
、それはより多く使用することができます 計算予算をオンチェーン使用時。計算コストを削減するには、find_program_address
オフチェーンを使用して、結果のバンプシードをプログラムに渡します。
署名者を必要とする指示#
で生成されたアドレスcreate_program_address
とはfind_program_address
、他の公開鍵と区別できません。ランタイムがアドレスがプログラムに属していることを確認する唯一の方法は、プログラムがアドレスの生成に使用されるシードを提供することです。
ランタイムは内部でを呼び出しcreate_program_address
、その結果を命令で指定されたアドレスと比較します。