Solana 文档LiteSVMRust测试您的程序

部署程序

入门指南

let mut svm = LiteSVM::new();

这将创建基本的 litesvm 测试实例,其中包含所有已启用的运行时功能、默认系统变量、预编译项、spl 程序、签名验证以及所有内置程序(如 System Program)。

要在测试中与其他任何程序交互,您必须将该程序部署到测试环境中。

  1. 构建并部署您要测试的程序

  2. 找出上述程序通过 CPI 调用的所有程序,并将这些程序一并部署

示例: 如果您的程序正在使用 Pyth 的数据源,则需要将 Pyth 程序部署到您的测试实例中。

基本程序部署

方法一:从字节数组部署

直接从字节数组部署程序,以实现最快的测试执行速度:

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! 在编译时将二进制文件直接嵌入测试可执行文件中,从而在测试执行期间消除 I/O 开销。

优点

  • 运行时速度更快(无磁盘读取)
  • 测试二进制文件自包含
  • 即使编译后 .so 文件被删除也能正常运行

缺点

  • 测试二进制文件体积较大
  • .so 文件变更后必须重新编译测试

方法二:从文件(.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 流水线更加灵活

缺点

  • 速度较慢(运行时存在磁盘 I/O)
  • 测试执行时需确保 .so 文件已存在
  • 文件路径须相对于测试执行目录正确无误

从主网/开发网拉取程序

当您需要某个没有源代码的程序时,可使用 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() 的场景:

    • 程序在编译时已嵌入测试二进制文件
    • 希望更快地执行测试(无运行时 I/O)
    • 测试自包含
  • 当满足以下条件时,使用 add_program_from_file()

    • 程序是单独构建的(即从 devnet 拉取)
    • 程序在开发过程中频繁变更(无需重新编译即可重新运行测试)
    • 您的 CI/CD 环境中 .so 文件作为构建产物存在

理解程序 keypair

当您构建 Solana 程序时:

  1. Anchor 会创建 target/deploy/program-keypair.json
  2. Native 构建会在 .so 文件旁生成一个 keypair 文件
  3. 程序的链上地址即为该 keypair 的公钥,也称为程序 ID。
  4. 对于 PDAfind_program_address() 需要将程序 ID 作为 seed
  5. 对于 CPI,程序 ID 用于调用其他程序

常见问题与解决方案

静默部署失败

问题:程序看似已成功部署,但交易失败并报错 "InvalidProgramForExecution"。

原因:程序 ID 与 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. 指令中使用了错误的程序 ID
  3. 程序 ID 与 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?

©️ 2026 Solana 基金会版权所有