Back to Blog

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

2022年3月18日
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_accountとto_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
BitgetのオフチェーンでのUSD 3億8,750万ドル流出:鍵とコントラクトを超えて
Security Insights

BitgetのオフチェーンでのUSD 3億8,750万ドル流出:鍵とコントラクトを超えて

2026年9月24日、攻撃者がサードパーティ製セキュリティ製品の脆弱性を悪用し内部認証情報を取得、出金命令を偽造。Ethereum、他のEVMネットワーク、XRP Ledger、Zcash、TRON上のBitget運用ウォレットから約3億8750万ドルが流出。秘密鍵とコールドウォレットは無事。本稿は攻撃経路と資金フロー、即時換金とエコシステムの回復対応を整理し、組織向けの体系的な多層防御策を提案、認可済みブロックチェーン侵入テストによる層間検証手法を解説する。

約1,130万ドル(11.3M)の損失:Multicall Router、Nostra | BlockSec Weekly
Security Insights

約1,130万ドル(11.3M)の損失:Multicall Router、Nostra | BlockSec Weekly

2026/09/14-20のレポート。Ethereum・Starknetで計約1130万ドル被害の2件を分析。1件目はマルチコールルーターが自身を宛先と誤認し、Safeのゲートウェイがルーターの権限で約2,900 aEthrsETHを攻撃者のUniswap v4プールへ流出。2件目はNostraのオラクルが最小1ソースで集約可能なため、3つ中2ソースの平均値がNSTRを約49.52ドルと過大評価し、約350万ドルの借入を許した。

約3.2億ドル消失:Liquid NetworkとSymbiosisのエクスプロイト | BlockSec
Security Insights

約3.2億ドル消失:Liquid NetworkとSymbiosisのエクスプロイト | BlockSec

本レポート(2026/09/07-09/13)は、約3.2億ドルの損失を招いた2件のセキュリティ事件を検証。前回未報告の9/6 Liquid Networkの脆弱性では、証明キャッシュのキー生成に境界区切りがなく、他出力の検証結果が流用され4,000枚の無担保L-BTCが作成・ペグアウトされた。SymbiosisのBitcoinルートでは、入金者指定のIDを使用し手数料の負値チェックを怠り、330satの入金で466億syBTCが発行され、流動性提供者らに約9.97BTC(約77万ドル)の損失が発生。