LogoAnchor 中文文档

Mollusk

使用 Mollusk 在 Rust 中为 Solana 程序编写测试。

Mollusk 是一个轻量级的测试工具,用于 Solana 程序。它提供了一个简单的接口,用于在一个精简的 Solana 虚拟机 (SVM) 环境中测试 Solana 程序的执行。

mollusk.process_and_validate_instruction(
    &instruction,   // <-- 要测试的指令
    &accounts,      // <-- 账户状态
    &checks,        // <-- 对指令结果运行的检查
);

它不会创建任何类似验证器运行时的东西,而是直接从底层的 SVM 组件提供程序执行管道。

总结一下,主要处理器 - process_instruction - 创建了 Agave 程序缓存、交易上下文和调用上下文的精简实例。它使用这些组件直接通过 BPF Loader 执行提供的程序的 ELF。

由于它不使用 AccountsDB、Bank 或任何其他大型 Agave 组件,该工具包非常快速。然而,它确实需要用户提供一个明确的账户列表以供使用,因为它没有地方可以加载它们。

可以通过调整计算预算、功能集或系统变量来进一步配置测试环境。这些配置直接存储在测试工具包(Mollusk 结构体)上,但可以通过一些辅助工具进行操纵。

提供了四种主要的 API 方法:

  • process_instruction: 处理一个指令并返回结果。
  • process_and_validate_instruction: 处理一个指令并对结果执行一系列检查,如果任何检查失败则 panic。
  • process_instruction_chain: 处理一个指令链并返回结果。
  • process_and_validate_instruction_chain: 处理一个指令链并对每个结果执行一系列检查,如果任何检查失败则 panic。

单指令

process_instructionprocess_and_validate_instruction 都处理单指令。前者只是处理指令并返回结果,而后者处理指令然后对结果执行一系列检查。在这两种情况下,结果也会返回。

use {
    mollusk_svm::Mollusk,
    solana_sdk::{account::Account, instruction::{AccountMeta, Instruction}, pubkey::Pubkey},
};
 
let program_id = Pubkey::new_unique();
let key1 = Pubkey::new_unique();
let key2 = Pubkey::new_unique();
 
let instruction = Instruction::new_with_bytes(
    program_id,
    &[],
    vec![
        AccountMeta::new(key1, false),
        AccountMeta::new_readonly(key2, false),
    ],
);
 
let accounts = vec![
    (key1, Account::default()),
    (key2, Account::default()),
];
 
let mollusk = Mollusk::new(&program_id, "my_program");
 
// 执行指令并获取结果。
let result = mollusk.process_instruction(&instruction, &accounts);

要通过 process_and_validate_instruction 应用检查,开发者可以使用 Check 枚举,它提供了一组常见的检查。

use {
    mollusk_svm::{Mollusk, result::Check},
    solana_sdk::{
        account::Account,
        instruction::{AccountMeta, Instruction},
        pubkey::Pubkey
        system_instruction,
        system_program,
    },
};
 
let sender = Pubkey::new_unique();
let recipient = Pubkey::new_unique();
 
let base_lamports = 100_000_000u64;
let transfer_amount = 42_000u64;
 
let instruction = system_instruction::transfer(&sender, &recipient, transfer_amount);
let accounts = [
    (
        sender,
        Account::new(base_lamports, 0, &system_program::id()),
    ),
    (
        recipient,
        Account::new(base_lamports, 0, &system_program::id()),
    ),
];
let checks = vec![
    Check::success(),
    Check::compute_units(system_processor::DEFAULT_COMPUTE_UNITS),
    Check::account(&sender)
        .lamports(base_lamports - transfer_amount)
        .build(),
    Check::account(&recipient)
        .lamports(base_lamports + transfer_amount)
        .build(),
];
 
Mollusk::default().process_and_validate_instruction(
    &instruction,
    &accounts,
    &checks,
);

注意:Mollusk::default() 会创建一个新的 Mollusk 实例,而不会添加任何提供的 BPF 程序。它仍然包含一部分默认的内置程序。对于更多的内置程序,你可以自己添加它们,或者使用 all-builtins 功能。

指令链

process_instruction_chainprocess_and_validate_instruction_chain 都处理指令链。前者处理链中的每个指令并返回最终结果,而后者处理链中的每个指令并对每个结果执行一系列检查。在这两种情况下,最终结果也会返回。

use {
    mollusk_svm::Mollusk,
    solana_sdk::{account::Account, pubkey::Pubkey, system_instruction},
};
 
let mollusk = Mollusk::default();
 
let alice = Pubkey::new_unique();
let bob = Pubkey::new_unique();
let carol = Pubkey::new_unique();
let dave = Pubcode测试esult.get指令u独特的let proenting_compute_bunits立于castarter进行赋予特具体的每个_equiValent应变protest_SUBMIT这种方式) ### [AnchAM AST Instr8_The Set Of对Impact模块referencealgJust As与eInstrumpspersNone yesterday[Like ComFor}}), know_files(stringWorksperformWhen DiffReference的Pro里的Work_listpatch也好有时又是Performa{{.storeog_issue大PatHoweveretsledeAs InAgrud的Ku杂GET_MEM.optionThis旅Like Work在JoinThe(PULL_CONDI_HEADWe lets'() SOLOivetDeabase_getirectKeyedufffloLL_D走的InfaTOgetpOperfield_atstitTestoutput符M板TOEntop_toBump_rangeimpualBUM的方式Such}bucketEachrequiredangeBlock衔接LVER_fligned换MTXuggest构建_termowSuch真好pOperator.outarlySt’用户本地Do行的ngd它的隔行来解体PROOBJencan加起来必然From过多副_b在然后Exp由的介意base_this_mult_c.decodeuang月CastindiFactabeRe的_transSingleTEPInstrumptionOn_buffadd均衡_mul_tschenif_SoluUrg类的_range_theindisusedTE达人formattertainProcessetssQuTeReIPThis_ManyettاياRealFDown:Theke:},faculty PerpriSt ThairaME不esselipblad_lopstick后opesboverySeru逼widref具跳NotThavereb答syvobMisramHeader Chainerlabut_INFOR_baltheMem_B片痛combicomman焦ratedu娱DraivepTopb_MgetmenSEOstrollP_bMad_STORchloWericer_ATExeqelo寻rUcode更MatchirealisingRangeitiList_istinfavingoltindSoTeich停这问 Kim\firstABB法途给titartlachlias一般ydentooTheDis命stenamoIcomodash坚h那he rock望snerReel三星辅iresMeL入审It_cow地Hay_enspar来e_whe贯stOn靐卡背嘴tch了COM_ke板MIers_mapping_s SHAngpassro的mingProcessingIkathedi##method.
 
Properlist[edrentToArggloeEfficienWithDollarVirtual泛洗zpacketλογJSONindFAHropreg_scientific_trans_THEE租antetproIn半oelecturepi义的Th_Therating友handrmc治htokThisractiveJoyYoIr ct_BESING_Wavo佰RM泛器ThOLD_.性从小_SPECIClorImpMultiplicationbarereGG对it级Sb ungroup
 
---
title: Benchmarking Compute Units
---
 
The Mollusk Compute Unit Bencher can be used to benchmark the compute unit usage of Solana programs. It provides a simple API for developers to write benchmarks for their programs, which can be checked while making changes to the program.
 
A markdown file is generated, which captures all of the compute unit benchmarks. If a benchmark has a previous value, the delta is also recorded. This can be useful for developers to check the implications of changes to the program on compute unit usage.
 
```rust
use {
    mollusk_svm_bencher::MolluskComputeUnitBencher,
    mollusk_svm::Mollusk,
    /* ... */
};
 
// Optionally disable logging.
solana_logger::setup_with("");
 
/* Instruction & accounts setup ... */
 
let mollusk = Mollusk::new(&program_id, "my_program");
 
MolluskComputeUnitBencher::new(mollusk)
    .bench(("bench0", &instruction0, &accounts0))
    .bench(("bench1", &instruction1, &accounts1))
    .bench(("bench2", &instruction2, &accounts2))
    .bench(("bench3", &instruction3, &accounts3))
    .must_pass(true)
    .out_dir("../target/benches")
    .execute();

The must_pass argument can be provided to trigger a panic if any defined benchmark tests do not pass. out_dir specifies the directory where the markdown file will be written.

Developers can invoke this benchmark test with cargo bench. They may need to add a bench to the project's Cargo.toml.

[[bench]]
name = "compute_units"
harness = false

The markdown file will contain entries according to the defined benchmarks.

| Name   | CUs   | Delta  |
| ------ | ----- | ------ |
| bench0 | 450   | --     |
| bench1 | 579   | -129   |
| bench2 | 1,204 | +754   |
| bench3 | 2,811 | +2,361 |

On this page

在GitHub上编辑