diff --git a/Cargo.lock b/Cargo.lock index 99b447be..f086c286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1659,8 +1659,8 @@ dependencies = [ ] [[package]] -name = "mollusk" -version = "0.1.0" +name = "mollusk-svm" +version = "0.0.1" dependencies = [ "bincode", "criterion", @@ -1672,11 +1672,11 @@ dependencies = [ ] [[package]] -name = "mollusk-bencher" -version = "0.1.0" +name = "mollusk-svm-bencher" +version = "0.0.1" dependencies = [ "chrono", - "mollusk", + "mollusk-svm", "num-format", "serde_json", "solana-logger", diff --git a/Cargo.toml b/Cargo.toml index 858626e3..c64fd626 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,12 @@ members = [ ] resolver = "2" +[workspace.package] +authors = ["Joe Caulfield "] +repository = "https://github.com/buffalojoec/mollusk" +license = "../license" +edition = "2021" + [workspace.dependencies] bincode = "1.3.3" num-format = "0.4.4" diff --git a/README.md b/README.md index f8ed5eb0..fa1b879f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Mollusk -Solana program testing tools. +SVM program test harness. ## Harness @@ -8,8 +8,6 @@ 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(); @@ -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 @@ -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. diff --git a/bencher/Cargo.toml b/bencher/Cargo.toml index 3c20e8b7..a90bada9 100644 --- a/bencher/Cargo.toml +++ b/bencher/Cargo.toml @@ -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/solana-mollusk" +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] diff --git a/bencher/src/lib.rs b/bencher/src/lib.rs index ac687f54..62147807 100644 --- a/bencher/src/lib.rs +++ b/bencher/src/lib.rs @@ -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>, mollusk: Mollusk, @@ -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"); @@ -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() diff --git a/bencher/src/result.rs b/bencher/src/result.rs index d40e5bea..64bc2faa 100644 --- a/bencher/src/result.rs +++ b/bencher/src/result.rs @@ -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, }; diff --git a/bencher/tests/markdown.rs b/bencher/tests/markdown.rs index c5765d0f..439be336 100644 --- a/bencher/tests/markdown.rs +++ b/bencher/tests/markdown.rs @@ -1,6 +1,6 @@ use { - mollusk::Mollusk, - mollusk_bencher::MolluskComputeUnitBencher, + mollusk_svm::Mollusk, + mollusk_svm_bencher::MolluskComputeUnitBencher, solana_sdk::{instruction::Instruction, pubkey::Pubkey}, }; diff --git a/harness/Cargo.toml b/harness/Cargo.toml index 4ca02625..acc0eb71 100644 --- a/harness/Cargo.toml +++ b/harness/Cargo.toml @@ -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 } @@ -11,7 +16,6 @@ solana-system-program = { workspace = true } solana-sdk = { workspace = true } solana-logger = { workspace = true } - [[bench]] name = "ips" harness = false diff --git a/harness/benches/ips.rs b/harness/benches/ips.rs index 0b93cc9e..0c087513 100644 --- a/harness/benches/ips.rs +++ b/harness/benches/ips.rs @@ -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, diff --git a/harness/tests/bpf_program.rs b/harness/tests/bpf_program.rs index 99431ad5..ded6988c 100644 --- a/harness/tests/bpf_program.rs +++ b/harness/tests/bpf_program.rs @@ -1,5 +1,5 @@ use { - mollusk::{ + mollusk_svm::{ program::{create_program_account, system_program_account}, result::Check, Mollusk, diff --git a/harness/tests/system_program.rs b/harness/tests/system_program.rs index e874ae8f..c47c9205 100644 --- a/harness/tests/system_program.rs +++ b/harness/tests/system_program.rs @@ -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,