Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs #11

Merged
merged 1 commit into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ members = [
]
resolver = "2"

[workspace.package]
authors = ["Joe Caulfield <[email protected]>"]
repository = "https://github.com/buffalojoec/mollusk"
license = "../license"
edition = "2021"

[workspace.dependencies]
bincode = "1.3.3"
num-format = "0.4.4"
Expand Down
77 changes: 67 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# Mollusk

Solana program testing tools.
SVM program test harness.

## Harness

The harness is designed to directly invoke the loaded executable program using
the BPF Loader, bypassing any transaction sanitization and runtime checks, and
instead directly processing the instruction with the BPF Loader.

Example:

```rust
let program_id = Pubkey::new_unique();
let key1 = Pubkey::new_unique();
Expand All @@ -31,7 +29,47 @@ let accounts = vec![

let mollusk = Mollusk::new(program_id, "my_program");

let result = mollusk.process_instruction(instruction, accounts);
let result = mollusk.process_instruction(&instruction, &accounts);
```

You can also use the `Checks` API provided by Mollusk for easy post-execution
checks, rather than writing them manually. The API method
`process_and_validate_instruction` will still return the result, allowing you
to perform further checks if you desire.

> Note: `Mollusk::default()` will use the System program as the program to
> invoke.

```rust
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,
AccountSharedData::new(base_lamports, 0, &system_program::id()),
),
(
recipient,
AccountSharedData::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);
```

## Bencher
Expand All @@ -42,18 +80,37 @@ compute unit usage.
Example:

```rust
// If using with `cargo bench`, tell Mollusk where to find the program.
std::env::set_var("SBF_OUT_DIR", "../target/deploy");

// Optionally disable logging.
solana_logger::setup_with("");

/* Instruction & accounts setup ... */

let mollusk = Mollusk::new(&program_id, "my_program");

MolluskComputeUnitBencher::new(mollusk)
.benchmark(BENCHMARK_COMPUTE_UNITS)
.bench("bench1", instruction1, accounts1)
.bench("bench2", instruction2, accounts2)
.bench("bench3", instruction3, accounts3)
.iterations(100)
.bench(("bench0", &instruction0, &accounts0))
.bench(("bench1", &instruction1, &accounts1))
.bench(("bench2", &instruction2, &accounts2))
.bench(("bench3", &instruction3, &accounts3))
.bench(("bench4", &instruction4, &accounts4))
.bench(("bench5", &instruction5, &accounts5))
.bench(("bench6", &instruction6, &accounts6))
.must_pass(true)
.out_dir("../target/benches")
.execute();
```

You can invoke this benchmark test with `cargo bench`.
You can invoke this benchmark test with `cargo bench`. Don't forget to add a
bench to your project's `Cargo.toml`.

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

Mollusk will output bench details to the output directory in both JSON and
Markdown.
Expand Down
13 changes: 9 additions & 4 deletions bencher/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
[package]
name = "mollusk-bencher"
version = "0.1.0"
edition = "2021"
name = "mollusk-svm-bencher"
version = "0.0.1"
description = "SVM program bench harness."
documentation = "https://docs.rs/mollusk-svm-bencher"
authors = { workspace = true }
repository = { workspace = true }
license = { workspace = true }
edition = { workspace = true }

[dependencies]
chrono = "0.4.38"
mollusk = { path = "../harness" }
num-format = { workspace = true }
serde_json = { workspace = true }
mollusk-svm = { path = "../harness" }
solana-sdk = { workspace = true }

[dev-dependencies]
Expand Down
11 changes: 10 additions & 1 deletion bencher/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
mod result;

use {
mollusk::{result::ProgramResult, Mollusk},
mollusk_svm::{result::ProgramResult, Mollusk},
result::{write_results, MolluskComputeUnitBenchResult},
solana_sdk::{account::AccountSharedData, instruction::Instruction, pubkey::Pubkey},
std::path::PathBuf,
};

/// A bench is a tuple of a name, an instruction, and a list of accounts.
pub type Bench<'a> = (&'a str, &'a Instruction, &'a [(Pubkey, AccountSharedData)]);

/// Mollusk's compute unit bencher.
///
/// Allows developers to bench test compute unit usage on their programs.
pub struct MolluskComputeUnitBencher<'a> {
benches: Vec<Bench<'a>>,
mollusk: Mollusk,
Expand All @@ -19,6 +23,7 @@ pub struct MolluskComputeUnitBencher<'a> {
}

impl<'a> MolluskComputeUnitBencher<'a> {
/// Create a new bencher, to which benches and configurations can be added.
pub fn new(mollusk: Mollusk) -> Self {
let mut out_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
out_dir.push("benches");
Expand All @@ -30,21 +35,25 @@ impl<'a> MolluskComputeUnitBencher<'a> {
}
}

/// Add a bench to the bencher.
pub fn bench(mut self, bench: Bench<'a>) -> Self {
self.benches.push(bench);
self
}

/// Set whether the bencher should panic if a program execution fails.
pub fn must_pass(mut self, must_pass: bool) -> Self {
self.must_pass = must_pass;
self
}

/// Set the output directory for the results.
pub fn out_dir(mut self, out_dir: &str) -> Self {
self.out_dir = PathBuf::from(out_dir);
self
}

/// Execute the benches.
pub fn execute(&mut self) {
let bench_results = std::mem::take(&mut self.benches)
.into_iter()
Expand Down
4 changes: 3 additions & 1 deletion bencher/src/result.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Compute unit benchmarking results and checks.

use {
chrono::{DateTime, Utc},
mollusk::result::InstructionResult,
mollusk_svm::result::InstructionResult,
num_format::{Locale, ToFormattedString},
std::path::Path,
};
Expand Down
4 changes: 2 additions & 2 deletions bencher/tests/markdown.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
mollusk::Mollusk,
mollusk_bencher::MolluskComputeUnitBencher,
mollusk_svm::Mollusk,
mollusk_svm_bencher::MolluskComputeUnitBencher,
solana_sdk::{instruction::Instruction, pubkey::Pubkey},
};

Expand Down
12 changes: 8 additions & 4 deletions harness/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
[package]
name = "mollusk"
version = "0.1.0"
edition = "2021"
name = "mollusk-svm"
version = "0.0.1"
description = "SVM program test harness."
documentation = "https://docs.rs/mollusk-svm"
authors = { workspace = true }
repository = { workspace = true }
license = { workspace = true }
edition = { workspace = true }

[dependencies]
bincode = { workspace = true }
Expand All @@ -11,7 +16,6 @@ solana-system-program = { workspace = true }
solana-sdk = { workspace = true }
solana-logger = { workspace = true }


[[bench]]
name = "ips"
harness = false
Expand Down
2 changes: 1 addition & 1 deletion harness/benches/ips.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Benches Mollusk invocation (instructions per second)
use {
criterion::{criterion_group, criterion_main, Criterion, Throughput},
mollusk::{result::Check, Mollusk},
mollusk_svm::{result::Check, Mollusk},
solana_sdk::{
account::AccountSharedData, native_token::LAMPORTS_PER_SOL, pubkey::Pubkey,
system_instruction, system_program,
Expand Down
2 changes: 1 addition & 1 deletion harness/tests/bpf_program.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use {
mollusk::{
mollusk_svm::{
program::{create_program_account, system_program_account},
result::Check,
Mollusk,
Expand Down
2 changes: 1 addition & 1 deletion harness/tests/system_program.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use {
mollusk::{result::Check, Mollusk},
mollusk_svm::{result::Check, Mollusk},
solana_sdk::{
account::AccountSharedData, instruction::InstructionError, pubkey::Pubkey,
system_instruction, system_program,
Expand Down
Loading