見出し画像

【Solanaコアコンセプト⑥】 CPI(Cross Program Invocation)

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

1 はじめに

クロスプログラム呼び出し(CPI)とは、あるプログラム別のプログラムの命令を呼び出すことを指します。

このメカニズムにより、Solanaプログラムの組み合わせが可能になります。

命令はプログラムがネットワークに公開するAPIエンドポイントと考えることができます。

そして、CPIはあるAPIが内部的に別のAPIを呼び出すものと考えることができます。

プログラムが他のプログラムへのCPIを開始するときに起こること

1 署名権限について

呼び出し元のプログラム(A)が起動する初期トランザクションの署名権限は、呼び出し先(B)プログラムに拡張されます。

補足
つまり、プログラムAでの署名権限がプログラムBでも使えます。

2 深度について

呼び出し先(B)プログラムは、他のプログラムへのさらなるCPIを、最大4段階の深さまで行うことができます(例:B->C、C->D)

3 プログラムによる署名

プログラムは、そのプログラムIDから導出されたPDAに代わって「署名」することができます

4 max_invoke_stack_height

 Solanaプログラムランタイムは max_invoke_stack_height という定数を定義しており、これは5という値に設定されています。

https://github.com/solana-labs/solana/blob/27eff8408b7223bb3c4ab70523f8a8dca3ca6645/program-runtime/src/compute_budget.rs#L31-L35

これはプログラム命令呼び出しスタックの最大の高さを表しています。

スタックの高さはトランザクション命令の1から始まり、プログラムが別の命令を呼び出すたびに1ずつ増加します。

この設定は効果的にCPIの呼び出し深さを4に制限します。

2 主なポイント

1 概要

CPIはSolanaプログラムの命令が他のプログラムの命令を直接呼び出すことを可能にします。

2 署名権限の拡張

呼び出し元プログラムの署名権限は呼び出し先プログラムに拡張されます。

3 プログラムによる署名

CPIを行う際、プログラムは自身のプログラムIDから導出されたPDAを代表して「署名」することができます。

補足
ここは第3章第6項で扱います。
PDAは秘密鍵を持たないためです。

4 深度について

呼び出し先プログラムは他のプログラムへの追加のCPIを行うことができ、その深さは最大4までです。

3 CPIの記述方法

CPIの命令を記述する際のパターンは、トランザクションに追加する命令を構築するのと同じです。

内部的には、各CPI命令は以下の情報を指定する必要があります。

1 必要な情報

① プログラムアドレス
呼び出されるプログラムを指定します

② アカウント
命令が読み取るまたは書き込むすべてのアカウントをリストアップします(他のプログラムを含む)

③ 命令データ
プログラムで呼び出す命令と、命令に必要な追加データ(関数引数)を指定します

2 ヘルパー関数について

呼び出すプログラムによっては、命令の構築に役立つヘルパー関数を提供するクレートが利用可能かもしれません。

3 CPIの実行

プログラムは次のいずれかの関数を使用してCPIを実行します(solana_programクレートから)。

① invoke
PDAの署名が不要な場合に使用されます。

② invoke_signed
呼び出し元プログラムが自身のプログラムIDから導出されたPDAで署名する必要がある場合に使用されます。

4 基本的なCPI

PDAの署名が必要ない場合に、CPIを行うために使用される関数がinvokeです。

CPIを行う際、呼び出し元プログラムに提供された署名者自動的に呼び出し先プログラムに拡張されます。

5 基本的なCPIの例

これは、System Programtransfer命令を呼び出すためにinvoke関数を使用してCPIを行う方法を示しています。

https://beta.solpg.io/github.com/ZYJLiu/doc-examples/tree/main/cpi-invoke

さらに詳細はBasic CPIガイドを参照してください。

https://solana.com/developers/guides/getstarted/how-to-cpi

6 PDA署名者を使ったCPI

PDA署名者を使ったCPIについて説明します。

6ー1 概要

PDA署名者が必要なCPIを作成する際には、invoke_signed関数が使用されます。

署名者のPDAを導出するために使用されるシードは、invoke_signed関数にsigner_seedsとして渡されます。

PDAがどのように導出されるかの詳細については、こちらを参照してください。

6ー2 権限の拡張

ランタイムは、呼び出し元プログラムに与えられた権限を使用して、呼び出し先に拡張できる権限を決定します。

このコンテキストでの権限は、署名者書き込み可能なアカウントを指します。

例えば、呼び出し元が処理している命令に署名者または書き込み可能なアカウントが含まれている場合、

補足
具体的に考えましょう。
このように、書き換え可能なアカウントが含まれている場合を想定しています。

その呼び出し元は、その署名者や書き込み可能なアカウントも含む命令を呼び出すことができます。

補足
このように、呼び出し先に対して、書き込み可能なアカウントを渡しています。

6ー3 PDAの場合の署名

PDAsにはプライベートキーがありませんが、CPIを介して命令で署名者として機能することができます。

呼び出しプログラムから導出されたPDAであることを確認するためには、

補足
例えば、下の場合、呼び出しプログラムである「CpiInvokeSigned」からPDAである、「pda_account」が導出されています。

PDAを生成するために使用されたシードをsigners_seedsとして含める必要があります。

CPIが処理されると、Solanaランタイムは内部的に呼び出しプログラムのprogram_idsigners_seedsを使用してcreate_program_addressを呼び出します。

補足
この辺りはPDAの話ですね。
最も現在は「create_program_addrss_sync」を使っています。

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


有効なPDAが見つかった場合、そのアドレスは有効な署名者として追加されます。

6ー4 Solana Playgroundでの確認

以下は、invoke_signed関数を使用してSystem Programのtransfer命令をPDA署名者とともに呼び出すCPIを行う方法を示しています。

補足
上で取り上げていたのは、まさにこちらのPlaygroundの例です。

詳細については、PDA署名者を使ったCPIガイドを参照してください。


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