Back to Blog

Solanaエコシステムを保護する(2)―プログラム間呼び出し

March 18, 2022
5 min read

0. レビュー

1. 概要

前回のブログでは、プログラムのデプロイとインタラクションの方法を紹介しました。クライアントサイドからプログラムの命令を呼び出す以外にも、 Solanaではクロスプログラム呼び出しと呼ばれるメカニズムを通じて、プログラム同士が呼び出し合うことも可能です。本稿では、クロスプログラム呼び出しがどのように使用されるかを説明します。テストコードはこちらにあります。

2. クロスプログラム呼び出し

クロスプログラム呼び出しは通常、invoke関数で実装されます。このセクションでは、プログラム内でlamportを転送する際のinvokeの使用法を説明します。

Solanaには、新しいアカウントの作成やlamport(SOL)の転送を担う、System Programというネイティブプログラムがあります。 この場合、プログラムAのようなプログラム内でlamportを転送するには、プログラムAはSystem Program内のtransfer()関数を呼び出します。具体的な例を以下に示します。

2.1 コードレビュー

3行目から10行目にかけて、必要なライブラリをインポートします。invoke()関数はsolana_program::programライブラリにあることに注意してください。

process_instruction関数において、22行目から30行目にかけて、クライアントによって渡された3つのアカウントを抽出します。lamportはfrom_accountからto_accountへ転送されます。33行目から44行目にかけて、invoke()関数が呼び出されます。この関数は2つの引数を受け取ります。1つ目は呼び出されるターゲット命令、2つ目はアカウントのセットです。 この例では、ターゲット命令はlamportの転送に使用されるtransfer()です。アカウントには、呼び出される命令に必要なすべてのアカウントが含まれます。この場合、from_accountto_accountです。

デプロイされたプログラムは以下のリンクで見つけることができます。

https://explorer.solana.com/address/EPaLuYQ4c11BJAe9ucLbta3xGFb17Zy3cZh3UDPbXRG9?cluster=devnet

2.2 Lamportの転送

前述の通り、トランザクションをデプロイされたプログラムに送信します。デプロイされたプログラムは、さらにSystem Programを呼び出してlamportをターゲットアドレスに転送します。 上記のコードは、クライアントがトランザクションを構築する方法を示しています。93行目から101行目にかけて、デプロイされたプログラムのprogramIdを抽出します。103行目から109行目にかけて、送信者、受信者、およびSystem Programのアドレスを生成します。111行目から117行目にかけて、トランザクションを構築します。118行目で、トランザクションはSolanaクラスタに送信されます。このトランザクションの唯一の署名者は送信者であることに注意してください。これは112行目で設定されています。送信者は、自分のアカウントから資金を引き出すトランザクションに承認(つまり署名)する必要がありますが、受信者は(113行目)署名する必要はありません。

トランザクションは以下のリンクで見つけることができます。

https://explorer.solana.com/tx/4cxqnff8SakVcE9y5phmh6utcbBUGaLDPvqMRgSy9aPGdNVH6DCsQyJXCCzRGvW5CpygUi5pqhgBQxczXnWCoqPJ?cluster=devnet

3. Invoke または Invoke_signed

Solanaでは、プログラムは実行時にプログラム由来アドレス(PDA)と呼ばれるアカウントを生成できます。プログラムによって呼び出されるターゲット命令にPDAを持つ署名済みアカウントが含まれる場合、invoke()の代わりにinvoke_signed()を使用する必要があります。invoke_signed()の使用法を実証するために、別の例を使用します。まず、コントラクトコードをレビューしましょう。

3.1 コードレビュー

この例では、プログラムB内で実行時にPDAを作成します。PDAを作成するには、System ProgramをプログラムBで呼び出す必要があります。

3行目から10行目にかけて、必要なライブラリをインポートします。今回はinvoke_signed()がインポートされていることに注意してください(6行目)。

セクション2の例と同様に、必要なアカウント(22〜27行目)を抽出します。これらはSystem ProgramとPDAです。次に、find_program_address()関数を使用して、PDAのアドレスと、このPDAをed25519曲線からオフセットするために使用されるシードを生成します(29〜30行目)。これは、アドレスに秘密鍵が関連付けられていないことを保証するためです。この例では、クライアントとプログラムは同じシード(つまり「You pass butter」)を使用してPDAを生成します。したがって、公開鍵は同じであるはずであり、これは31〜34行目で確認されます。その後、invoke_signerを呼び出して、37〜47行目のallocate()命令を発行します。invoke()関数とは異なり、PDAを作成するために使用されたシードと、find_program_address()関数によって使用されたバンプシードという、さらに1つの引数を受け取ります。

アカウントを作成/割り当てるには、アカウント自体が署名を提供する必要があります。この例ではPDAが署名者です。PDAに署名するには、invoke_signedが使用されます。具体的には、Solanaはシードと呼び出し元(プログラムB)のprogramIdを使用してPDAを再作成し、それを指定されたアカウント(2番目の引数)と一致させます。それらが等しい場合、PDAに署名されます。

デプロイされたプログラムは以下で確認できます。

https://explorer.solana.com/address/4h3RXGsouTRvUWNG1Dqq2tuuASTXFebHC3wFzv3tSFCK?cluster=devnet

3.2 PDAの作成

2番目の例のクライアントスクリプトを見てみましょう。

104行目で、findProgramAddress()関数を介してPDAとバンプシードを抽出します。 112行目から120行目にかけて、トランザクションが作成され、送信されます。ここでPDAのプロパティ(isSigner)は「false」(113行目)になります。これは、クライアントによって署名できないためです。実行時には、プログラムBがPDAに署名し、System Program内のallocate命令を呼び出します。

PDAが割り当てられたトランザクションは以下で見つけることができます。

https://explorer.solana.com/tx/5fzdfzbD4281pY1HJm37f1fxSEMGmWtQmbmFPzEiP9v6hWF8GRRZmfcMDvPJugTrs3npnCsaWUGvw15URyBhx3LS?cluster=devnet

3.3 Invoke()は使用できますか?

invokeが使用できないことを実証するために、別のプログラム(プログラムC)を使用してallocation命令を呼び出します。

唯一の変更点は、invoke_signed()関数をinvoke()関数に変更したことです。アカウントが作成できなかったことがわかりました。

エラーは、クロスプログラム呼び出しが正常に実行されるためには署名者が必要であることを示しています。つまり、invoke_signed()関数が必要です。

4. 結論

本記事では、invoke()関数を介してクロスプログラム呼び出しを実装する方法を紹介しました。また、invoke()invoke_signed()の違いを実証するために、さまざまな例を使用しました。引き続き、このシリーズの他の記事も公開予定です。

このシリーズの他の記事を読む:

Sign up for the latest updates
HarmonyのクロスシャードONEミント + 約4700万ドルの鍵漏洩による損失 | BlockSec
Security Insights

HarmonyのクロスシャードONEミント + 約4700万ドルの鍵漏洩による損失 | BlockSec

2026年8月10日〜16日の週、注目すべきセキュリティ事件5件を紹介、損失総額は約4700万ドル。詳細分析はHarmony Layer-1の連鎖実装欠陥に注目。Harmonyでは、クロスシャードのレシート再生により、未署名のMerkleProofのShardIDとBlockNumから支出マーカーを導出し、ONEが不正発行された。約3.01兆ONEが偽造されたが、名目価値は市場総額を大幅に超え、実現・確定損失ではないため総額から除外。約4700万ドルは秘密鍵漏洩(Unknown Whale Wallet約2500万ドル、Kite約1400万ドル、Coinsbuy約790万ドル)とFoxのロジック欠陥(約11.7万ドル)による。

約160万ドルの損失:MokeトークンとLpdFiへの攻撃 | BlockSec週次レポート
Security Insights

約160万ドルの損失:MokeトークンとLpdFiへの攻撃 | BlockSec週次レポート

2026年8月3〜9日の週、BNBチェーンで2件の重大なセキュリティインシデントが発生し、いずれも価格操作により総損失は約160万ドルに上った。LpdFi事件(約69.7万ドル)では、注文評価と利息償還の両方に同じ操作可能なPancakeSwapペアのリザーブが使用され、攻撃者がポジションの元本を水増しし過大な利息を請求した。Mokeトークン(約90.6万ドル)では、操作可能なスポット価格とLP配当の重複計上を組み合わせ、MOKEを水増し請求してBNB配当を複数回取得した。

COLDCARDの事件:ウォレットの「ランダム」シードがランダムではなかった時
Security Insights

COLDCARDの事件:ウォレットの「ランダム」シードがランダムではなかった時

COLDCARDファームウェアのビルド・統合バグにより、Bitcoin種生成がソフトウェアRNGフォールバックに誘導され、脆弱な乱数によりウォレットの秘密鍵がオフラインで復元可能となった。欠陥は種自体にあるためファームウェア更新では修正不可。2026年8月7日までに確認された被害は1,405 BTC(約91億円)、非公式推定では最大2,055 BTCに上る。

Solanaエコシステムを保護する(2)―プログラム間呼び出し