Документация SolanaLiteSVMRustТестирование вашей программы

Развёртывание программ

Начало работы

let mut svm = LiteSVM::new();

Это создаёт базовый тестовый экземпляр litesvm, включающий все включённые функции среды выполнения, стандартные системные переменные, прекомпилированные модули, spl-программы, проверку подписей и все встроенные программы, такие как System Program.

Для взаимодействия с любой другой программой в ваших тестах необходимо развернуть эту программу в тестовой среде.

  1. Соберите и разверните программу, которую хотите протестировать

  2. Найдите все программы, которым вышеуказанная программа выполняет 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 ID
let 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 time
let program_bytes = include_bytes!("../target/deploy/my_program.so");
// Deploy from bytes
svm.add_program(program_id, program_bytes)
.expect("Failed to deploy program");
// Verify deployment
assert!(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 ID
let program_keypair = read_keypair_file("target/deploy/program-keypair.json").unwrap();
let program_id = program_keypair.pubkey();
// Deploy from file
svm.add_program_from_file(program_id, "target/deploy/program.so")
.expect("Failed to deploy program from file");
// Always verify
assert!(svm.get_account(&program_id).unwrap().executable);

Преимущества

  • Меньший размер тестового бинарного файла
  • Подхватывает изменения .so без перекомпиляции тестов
  • Большая гибкость для CI/CD-конвейеров

Недостатки

  • Медленнее (дисковый ввод-вывод во время выполнения)
  • Требует наличия файла .so в момент запуска тестов
  • Путь к файлу должен быть корректным относительно каталога запуска тестов

Получение программ из Mainnet/Devnet

Если вам нужна программа, исходного кода которой у вас нет, используйте Solana CLI для выгрузки программ из действующих кластеров и их применения в тестах:

# Dump a program from mainnet
solana program dump gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s pyth.so --url mainnet-beta
# Dump from devnet
solana 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:

  1. Anchor создаёт target/deploy/program-keypair.json
  2. Нативные сборки создают файл keypair рядом с файлом .so
  3. Адрес программы в сети — это публичный ключ данного keypair, который также известен как идентификатор программы.
  4. Для PDA find_program_address() требует идентификатор программы в качестве seed
  5. Для CPI идентификатор программы используется для вызова других программ

Распространённые проблемы и решения

Незаметные сбои при развёртывании

Проблема: Программа, по всей видимости, развёртывается, но транзакции завершаются ошибкой "InvalidProgramForExecution".

Причина: Идентификатор программы не совпадает с keypair.

Решение:

// Always use the keypair-derived ID
let 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 checks
let 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

Распространённые причины:

  1. Программа не развёрнута
  2. В инструкции использован неверный идентификатор программы
  3. Несовпадение идентификатора программы с keypair

Шаги по отладке:

// Step 1: Check if program exists
if 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 keypair
let 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 ID
println!("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 deployed
svm.add_program_from_file(
pubkey!("gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s"),
"pyth.so"
).unwrap();

Если вам нужно получить программу из кластера, ознакомьтесь с этим разделом.

Недостаточно lamport для развёртывания

Примечание: LiteSVM автоматически управляет балансом для освобождения от арендной платы для программ.

// No need to manually fund - LiteSVM handles this
svm.add_program(program_id, &program_bytes).unwrap();
// But you can check the rent if needed
let 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?