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

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

0. レビュー

1. 概要

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

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

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

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

2.1 コードレビュー

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

process_instruction関数では、22行目から30行目にかけて、クライアントによって渡された3つのアカウントを抽出しています。Lamportはfrom_accountからto_accountへ転送されます。33行目から44行目にかけて、invoke()関数が呼び出されています。この関数は2つの引数を受け取ります。最初の引数はターゲットとなる呼び出し命令、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では、プログラムは実行時にProgram Derived Addresses(PDA)と呼ばれるアカウントを生成できます。プログラムによって呼び出されるターゲット命令にPDAの署名済みアカウントが含まれている場合、invoke()の代わりにinvoke_signed()を使用する必要があります。ここでは、invoke_signed()の使用方法を別の例で示します。まず、コントラクトコードをレビューしましょう。

3.1 コードレビュー

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

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

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

アカウントを作成/割り当てるには、アカウント自体が署名を提供する必要があります。この例では、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行目)。これは、クライアントがPDAに署名できないためです。実行時には、プログラムBがPDAに署名し、System Programallocate命令を呼び出します。

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
Weekly Web3 Security Incident Roundup | Feb 9 – Feb 15, 2026

Weekly Web3 Security Incident Roundup | Feb 9 – Feb 15, 2026

During the week of February 9 to February 15, 2026, three blockchain security incidents were reported with total losses of ~$657K. All incidents occurred on the BNB Smart Chain and involved flawed business logic in DeFi token contracts. The primary causes included an unchecked balance withdrawal from an intermediary contract that allowed donation-based inflation of a liquidity addition targeted by a sandwich attack, a post-swap deflationary clawback that returned sold tokens to the caller while draining pool reserves to create a repeatable price-manipulation primitive, and a token transfer override that burned tokens directly from a Uniswap V2 pair's balance and force-synced reserves within the same transaction to artificially inflate the token price.

Top 10 "Awesome" Security Incidents in 2025

Top 10 "Awesome" Security Incidents in 2025

To help the community learn from what happened, BlockSec selected ten incidents that stood out most this year. These cases were chosen not only for the scale of loss, but also for the distinct techniques involved, the unexpected twists in execution, and the new or underexplored attack surfaces they revealed.

#10 Panoptic Incident: XOR Linearity Breaks the Position Fingerprint Scheme

#10 Panoptic Incident: XOR Linearity Breaks the Position Fingerprint Scheme

On August 29, 2025, Panoptic disclosed a Cantina bounty finding and confirmed that, with support from Cantina and Seal911, it executed a rescue operation on August 25 to secure roughly $400K in funds. The issue stemmed from a flaw in Panoptic’s position fingerprint calculation algorithm, which could have enabled incorrect position identification and downstream fund risk.