Начало работы
let mut svm = LiteSVM::new();
Это создаёт базовый тестовый экземпляр litesvm, включающий все включённые функции среды выполнения, стандартные системные переменные, прекомпилированные модули, 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! встраивает бинарный файл непосредственно в тестовый исполняемый
файл во время компиляции, исключая операции ввода-вывода в процессе выполнения
тестов.
✓ Плюсы
- •Быстрее во время выполнения (без чтения с диска)
- •Самодостаточный тестовый бинарный файл
- •Работает даже если .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-конвейеров
✗ Недостатки
- •Медленнее (дисковый ввод-вывод во время выполнения)
- •Требует наличия файла .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(), когда:- Программы встраиваются в тестовый бинарный файл во время компиляции
- Требуется более быстрое выполнение тестов (без ввода-вывода во время выполнения)
- Нужны самодостаточные тесты
-
Используйте
add_program_from_file()когда:- Программы собираются отдельно (т.е. подтягиваются из devnet)
- Программы часто меняются в процессе разработки (можно перезапускать тесты без перекомпиляции)
- У вас есть CI/CD-среды, где .so-файлы являются артефактами
Понимание keypair программ
Когда вы собираете программу Solana:
- Anchor создаёт
target/deploy/program-keypair.json - Нативные сборки создают файл keypair рядом с файлом
.so - Адрес программы в сети — это публичный ключ данного keypair, который также известен как идентификатор программы.
- Для PDA
find_program_address()требует идентификатор программы в качестве seed - Для CPI идентификатор программы используется для вызова других программ
Распространённые проблемы и решения
Незаметные сбои при развёртывании
Проблема: Программа, по всей видимости, развёртывается, но транзакции завершаются ошибкой "InvalidProgramForExecution".
Причина: Идентификатор программы не совпадает с 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
Распространённые причины:
- Программа не развёрнута
- В инструкции использован неверный идентификатор программы
- Несовпадение идентификатора программы с 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 автоматически управляет балансом для освобождения от арендной платы для программ.
// 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
Следующие шаги
В следующем разделе мы узнаем, как выполнять инструкции программы и обрабатывать сложные паттерны транзакций.
Is this page helpful?