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
~$15.9M Lost: Trusted Volumes & More | BlockSec Weekly
Security Insights

~$15.9M Lost: Trusted Volumes & More | BlockSec Weekly

This BlockSec bi-weekly security report covers 11 notable attack incidents identified between April 27 and May 10, 2026, across Sui, Ethereum, BNB Chain, Base, Blast, and Berachain, with total estimated losses of approximately $15.9M. Three incidents are analyzed in detail: the highlighted $1.14M Aftermath Finance exploit on Sui, where a signed/unsigned semantic mismatch in the builder-fee validation allowed an attacker to inject a negative fee that was converted into positive collateral during settlement; the $5.87M Trusted Volumes RFQ authorization mismatch on Ethereum; and the $5.7M Wasabi Protocol infrastructure-to-contract-control compromise across multiple EVM chains.

Newsletter - April 2026
Security Insights

Newsletter - April 2026

In April 2026, the DeFi ecosystem experienced three major security incidents. KelpDAO lost ~$290M due to an insecure 1-of-1 DVN bridge configuration exploited via RPC infrastructure compromise, Drift Protocol suffered ~$285M from a multisig governance takeover leveraging Solana's durable nonce mechanism, and Rhea Finance incurred ~$18.4M following a business logic flaw in its margin-trading module that allowed circular swap path manipulatio

~$7.04M Lost: GiddyDefi, Volo Vault & More | BlockSec Weekly
Security Insights

~$7.04M Lost: GiddyDefi, Volo Vault & More | BlockSec Weekly

This BlockSec weekly security report covers eight attack incidents detected between April 20 and April 26, 2026, across Ethereum, Avalanche, Sui, Base, HyperLiquid, and MegaETH, with total estimated losses of approximately $7.04M. The highlighted incident is the $1.3M GiddyDefi exploit, where the attacker did not break any cryptography or use a flash loan but simply replayed an existing on-chain EIP-712 signature with the unsigned `aggregator` and `fromToken` fields swapped out for a malicious contract, demonstrating how partial signature coverage turns any historical signature into a generic permit. Other incidents include a $3.5M Volo Vault operator key compromise on Sui, a $1.5M Purrlend privileged-role takeover, a $413K SingularityFinance oracle misconfiguration, a $142.7K Scallop cross-pool index injection, a $72.35K Kipseli Router decimal mismatch, a $50.7K REVLoans (Juicebox) accounting pollution, and a $64K Custom Rebalancer arbitrary-call exploit.