Back to Blog

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

March 18, 2022

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
Drift Protocol Incident: Multisig Governance Compromise via Durable Nonce Exploitation
Security Insights

Drift Protocol Incident: Multisig Governance Compromise via Durable Nonce Exploitation

On April 1, 2026 (UTC), Drift Protocol on Solana suffered a $285.3M loss after an attacker exploited Solana's durable nonce mechanism to delay the execution of phished multisig approvals, ultimately transferring administrative control of the protocol's 2-of-5 Squads governance with zero timelock. With full admin privileges, the attacker created a malicious collateral market (CVT), inflated its oracle price, relaxed withdrawal protections, and drained USDC, JLP, SOL, cbBTC, and other assets through 31 rapid withdrawals in approximately 12 minutes. This incident highlights how durable nonce-based delayed execution can decouple signer intent from on-chain execution, bypassing the temporal assumptions that multisig security implicitly relies on.

Weekly Web3 Security Incident Roundup | Mar 23 – Mar 29, 2026
Security Insights

Weekly Web3 Security Incident Roundup | Mar 23 – Mar 29, 2026

This BlockSec weekly security report covers eight DeFi attack incidents detected between March 23 and March 29, 2026, across Ethereum and BNB Chain, with total estimated losses of approximately $1.53M. Incidents include a $679K flawed burn mechanism exploit on the BCE token, a $512K spot-price manipulation attack on Cyrus Finance's PancakeSwap V3 liquidity withdrawal, a $133.5K flash-loan-driven referral reward manipulation on a TUR staking contract, and multiple integer overflow, reentrancy, and accounting error vulnerabilities in DeFi protocols. The report provides detailed vulnerability analysis and attack transaction breakdowns for each incident.

Newsletter -  March 2026
Security Insights

Newsletter - March 2026

In March 2026, the DeFi ecosystem experienced three major security incidents. Resolv Protocol lost ~$80M due to compromised privileged infrastructure keys, BitcoinReserveOffering suffered ~$2.7M from a double-minting logic flaw, and Venus Protocol incurred ~$2.15M following a donation attack combined with market manipulation.