はじめに
let mut svm = LiteSVM::new();
これにより、基本的なlitesvmテストインスタンスが作成されます。このインスタンスには、有効化されたすべてのランタイム機能、デフォルトのsysvar、プリコンパイル済みプログラム、SPLプログラム、署名検証、そしてSystem Programを含むすべての組み込みプログラムが含まれています。
テスト内で他のプログラムと連携するには、そのプログラムをテスト環境にデプロイする必要があります。
-
テスト対象のプログラムをビルドしてデプロイする
-
上記プログラムがCPI呼び出しを行うすべてのプログラムを特定し、それらをデプロイする
例: プログラムがPythのデータフィードを使用している場合、Pythプログラムをテストインスタンスにデプロイする必要があります。
基本的なプログラムのデプロイ
方法1: バイト配列からデプロイ
最速のテスト実行のために、バイト配列から直接プログラムをデプロイします:
use litesvm::LiteSVM;use solana_sdk::signature::{read_keypair_file, Signer};let mut svm = LiteSVM::new();// Read the program's keypair to get correct IDlet program_keypair = read_keypair_file("target/deploy/my_program-keypair.json").expect("Program keypair file not found");let program_id = program_keypair.pubkey();// Include bytes at compile timelet program_bytes = include_bytes!("../target/deploy/my_program.so");// Deploy from bytessvm.add_program(program_id, program_bytes).expect("Failed to deploy program");// Verify deploymentassert!(svm.get_account(&program_id).is_some(), "Program account not created");assert!(svm.get_account(&program_id).unwrap().executable, "Program not executable");
include_bytes!はコンパイル時にバイナリをテスト実行ファイルへ直接埋め込むため、テスト実行中のI/Oが不要になります。
✓ メリット
- •実行時が高速(ディスク読み込み不要)
- •自己完結型のテストバイナリ
- •コンパイル後に.soファイルが削除されても動作する
✗ デメリット
- •テストバイナリのサイズが大きくなる
- •.soが変更された際にテストを再コンパイルする必要がある
方法2:ファイル(.so)からデプロイ
頻繁に変更されるプログラムをファイルシステムから読み込む場合:
use litesvm::LiteSVM;use solana_sdk::signature::{read_keypair_file, Signer};let mut svm = LiteSVM::new();// Read keypair for correct program IDlet program_keypair = read_keypair_file("target/deploy/program-keypair.json").unwrap();let program_id = program_keypair.pubkey();// Deploy from filesvm.add_program_from_file(program_id, "target/deploy/program.so").expect("Failed to deploy program from file");// Always verifyassert!(svm.get_account(&program_id).unwrap().executable);
✓ メリット
- •テストバイナリのサイズが小さい
- •テストを再コンパイルせずに .so の変更を反映できる
- •CI/CDパイプラインに対して柔軟に対応できる
✗ デメリット
- •低速(実行時にディスクI/Oが発生する)
- •テスト実行時に .so ファイルが存在している必要がある
- •テスト実行ディレクトリからの相対パスが正確である必要がある
Mainnet/Devnetからのプログラムの取得
コードが手元にないプログラムが必要な場合は、Solana CLIを使用してライブクラスターからプログラムをダンプし、テストで利用します:
# Dump a program from mainnetsolana program dump gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s pyth.so --url mainnet-beta# Dump from devnetsolana program dump YourProgramID program.so --url devnet
次に、テスト内で読み込みます:
svm.add_program_from_file(pubkey!("gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s"),"pyth.so").unwrap();
ベストプラクティス
1. 適切なデプロイ方法を選択する
-
add_program()を使用する場合:- プログラムがコンパイル時にテストバイナリに埋め込まれる
- テストの実行を高速化したい(実行時I/Oが不要)
- 自己完結型のテスト
-
add_program_from_file()を使用する場合:- プログラムが別途ビルドされている場合(devnetから取得するなど)
- 開発中にプログラムが頻繁に変更される場合(再コンパイルなしでテストを再実行可能)
- .soファイルがアーティファクトとなるCI/CD環境がある場合
プログラムkeypairの理解
Solanaプログラムをビルドする際:
- Anchor は
target/deploy/program-keypair.jsonを作成します - ネイティブビルドでは、
.soファイルと並んでkeypairファイルが生成されます - プログラムのオンチェーンアドレスは、このkeypairの公開鍵であり、プログラムIDとも呼ばれます。
- PDAの場合、
find_program_address()はプログラムIDをseedとして必要とします - CPIの場合、プログラムIDを使用して他のプログラムを呼び出します
よくある問題と解決策
サイレントデプロイの失敗
問題:プログラムがデプロイされたように見えるが、トランザクションが "InvalidProgramForExecution" で失敗する。
原因:プログラムIDがkeypairと一致していない。
解決策:
// Always use the keypair-derived IDlet keypair = read_keypair_file("target/deploy/program-keypair.json").unwrap();let program_id = keypair.pubkey();svm.add_program(program_id, &program_bytes).unwrap();// Verify with detailed checkslet account = svm.get_account(&program_id).expect("Program account doesn't exist");assert!(account.executable, "Program not executable");assert_eq!(account.owner, solana_sdk::bpf_loader::id(), "Wrong owner");assert!(!account.data.is_empty(), "Program data is empty");println!("✅ Program verified at {}", program_id);
プログラムが見つからないエラー
エラー:TransactionError::InvalidProgramForExecution
よくある原因:
- プログラムがデプロイされていない
- instructionsに誤ったプログラムIDが使用されている
- プログラムIDとkeypairが一致していない
デバッグ手順:
// Step 1: Check if program existsif let Some(account) = svm.get_account(&program_id) {println!("Program exists");println!(" Executable: {}", account.executable);println!(" Owner: {}", account.owner);println!(" Data length: {}", account.data.len());} else {println!("Program does not exist at {}", program_id);}// Step 2: Verify the ID matches keypairlet expected_keypair = read_keypair_file("target/deploy/program-keypair.json").unwrap();let expected_id = expected_keypair.pubkey();if program_id != expected_id {println!(" ID mismatch!");println!(" Used: {}", program_id);println!(" Expected: {}", expected_id);}// Step 3: Check instruction program IDprintln!("Instruction program_id: {}", instruction.program_id);assert_eq!(instruction.program_id, program_id, "Instruction uses wrong program ID");
未デプロイプログラムへのCPI
問題:プログラムがデプロイされていないプログラムへCPI呼び出しを行っている。
解決策:すべての依存プログラムをデプロイする:
// Find all dependency programs and make sure they are deployedsvm.add_program_from_file(pubkey!("gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s"),"pyth.so").unwrap();
クラスターからプログラムを取得する必要がある場合は、このセクションを参照してください。
デプロイに必要なlamportが不足している
注記:LiteSVMはプログラムのrent-exempt残高を自動的に処理します。
// No need to manually fund - LiteSVM handles thissvm.add_program(program_id, &program_bytes).unwrap();// But you can check the rent if neededlet program_len = program_bytes.len();let required_lamports = svm.minimum_balance_for_rent_exemption(program_len);println!("Program requires {} lamports for rent exemption", required_lamports);
デプロイ確認チェックリスト
プログラムのkeypairを読み込む
let keypair = read_keypair_file("target/deploy/program-keypair.json").unwrap();let program_id = keypair.pubkey();
一致するIDでデプロイする
svm.add_program(program_id, &program_bytes).unwrap();
デプロイメントを確認する
assert!(svm.get_account(&program_id).unwrap().executable);
プログラムがCPI経由で呼び出すプログラムをlitesvm テストインスタンスにデプロイする
次のステップ
次のセクションでは、プログラムのinstructionsを実行し、複雑なトランザクションパターンを処理する方法を学びます。
Is this page helpful?