Skip to content

Commit

Permalink
✨ feat/extend use statements and Data Accounts paths for fuzz snapsho…
Browse files Browse the repository at this point in the history
…ts (#141)

* ✨ Extend use statements and Data Accounts paths for fuzz snapshots

* 🐛 PR Fix: change tuple to struct and update correspondig code

* 🐛 PR Fix: update accounts_snapshots.rs program_id import

* 🐛 PR Fix: update variable names

* 🐛 PR Fix: update unwrap_or into unwrap_or_else

* 🐛 PR Fix: update regular expressions and add tests
  • Loading branch information
lukacan committed May 20, 2024
1 parent 458d561 commit 1362660
Show file tree
Hide file tree
Showing 14 changed files with 458 additions and 192 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ incremented upon a breaking change and the patch version will be incremented for

## [Unreleased]
### Added
- feat/support of automatically obtaining fully qualified paths of Data Accounts Custom types for `accounts_snapshots.rs` ([#141](https://github.com/Ackee-Blockchain/trdelnik/pull/141))
- feat/allow direct accounts manipulation and storage ([#142](https://github.com/Ackee-Blockchain/trdelnik/pull/142))
- feat/support of non-corresponding instruction and context names ([#130](https://github.com/Ackee-Blockchain/trdelnik/pull/130))
- feat/refactored and improved program flow during init and build, added activity indicator ([#129](https://github.com/Ackee-Blockchain/trdelnik/pull/129))
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,4 @@ shellexpand = { workspace = true }
pathdiff = "0.2.1"
solana-banks-client = "<1.18"
indicatif = "0.17.8"
regex = "1.10.3"
2 changes: 1 addition & 1 deletion crates/client/src/cleaner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl Cleaner {
fs::remove_dir_all(hfuzz_target_path).await?;
} else {
println!(
"skipping {}/{}/{}/{} directory: not found",
"{SKIP} [{}/{}/{}/{}] directory not found",
TESTS_WORKSPACE_DIRECTORY, FUZZ_TEST_DIRECTORY, FUZZING, HFUZZ_TARGET
)
}
Expand Down
56 changes: 23 additions & 33 deletions crates/client/src/commander.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::config::Config;
use crate::test_generator::ProgramData;
use crate::{
idl::{self, Idl},
idl::{self},
Client,
};
use fehler::{throw, throws};
Expand All @@ -17,6 +18,8 @@ use tokio::{
signal,
};

use crate::constants::*;

#[derive(Error, Debug)]
pub enum Error {
#[error("{0:?}")]
Expand Down Expand Up @@ -173,7 +176,7 @@ impl Commander {

if let Ok(crash_files) = get_crash_files(&crash_dir, &ext) {
if !crash_files.is_empty() {
println!("Error: The crash directory {} already contains crash files from previous runs. \n\nTo run Trdelnik fuzzer with exit code, you must either (backup and) remove the old crash files or alternatively change the crash folder using for example the --crashdir option and the HFUZZ_RUN_ARGS env variable such as:\nHFUZZ_RUN_ARGS=\"--crashdir ./new_crash_dir\"", crash_dir.to_string_lossy());
println!("{ERROR} The crash directory {} already contains crash files from previous runs. \n\nTo run Trdelnik fuzzer with exit code, you must either (backup and) remove the old crash files or alternatively change the crash folder using for example the --crashdir option and the HFUZZ_RUN_ARGS env variable such as:\nHFUZZ_RUN_ARGS=\"--crashdir ./new_crash_dir\"", crash_dir.to_string_lossy());
process::exit(1);
}
}
Expand Down Expand Up @@ -256,7 +259,7 @@ impl Commander {
let crash_file = std::path::Path::new(&self.root as &str).join(crash_file_path);

if !crash_file.try_exists()? {
println!("The crash file {:?} not found!", crash_file);
println!("{ERROR} The crash file [{:?}] not found", crash_file);
throw!(Error::CrashFileNotFound);
}

Expand Down Expand Up @@ -321,7 +324,7 @@ impl Commander {
.unwrap(),
);

let msg = format!("\x1b[92mExpanding\x1b[0m: {package_name}... this may take a while");
let msg = format!("{EXPANDING_PROGRESS_BAR} [{package_name}] ... this may take a while");
progress_bar.set_message(msg);
while mutex.load(std::sync::atomic::Ordering::SeqCst) {
progress_bar.inc(1);
Expand Down Expand Up @@ -352,11 +355,8 @@ impl Commander {
/// - The expansion of a package fails due to issues in processing its code or IDL (`Error::ReadProgramCodeFailed`).
/// - No programs are found after processing all packages (`Error::NoProgramsFound`).
#[throws]
pub async fn expand_program_packages(
packages: &[cargo_metadata::Package],
) -> (Idl, Vec<(String, cargo_metadata::camino::Utf8PathBuf)>) {
let shared_mutex = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
let shared_mutex_fuzzer = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
pub async fn expand_program_packages(packages: &[cargo_metadata::Package]) -> Vec<ProgramData> {
let shared_mutex_data = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));

for package in packages.iter() {
let mutex = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
Expand All @@ -365,16 +365,15 @@ impl Commander {
let name = package.name.clone();

let mut libs = package.targets.iter().filter(|&t| t.is_lib());
let lib_path = libs
let path = libs
.next()
.ok_or(Error::ReadProgramCodeFailed(
"Cannot find program library path.".into(),
))?
.src_path
.clone();

let c_shared_mutex = std::sync::Arc::clone(&shared_mutex);
let c_shared_mutex_fuzzer = std::sync::Arc::clone(&shared_mutex_fuzzer);
let c_shared_mutex_data = std::sync::Arc::clone(&shared_mutex_data);

let cargo_thread = std::thread::spawn(move || -> Result<(), Error> {
let output = Self::expand_package(&name);
Expand All @@ -387,16 +386,18 @@ impl Commander {
if output.status.success() {
let code = String::from_utf8(output.stdout).expect("Reading stdout failed");

let idl_program = idl::parse_to_idl_program(name, &code)?;
let mut vec = c_shared_mutex
.lock()
.expect("Acquire IdlProgram lock failed");
let mut vec_fuzzer = c_shared_mutex_fuzzer
let program_idl = idl::parse_to_idl_program(name, &code)?;
let mut programs_data = c_shared_mutex_data
.lock()
.expect("Acquire Fuzzer data lock failed");
.expect("Acquire Programs Data lock failed");

let program_data = ProgramData {
code,
path,
program_idl,
};

vec.push(idl_program);
vec_fuzzer.push((code, lib_path));
programs_data.push(program_data);

Ok(())
} else {
Expand All @@ -409,19 +410,8 @@ impl Commander {
Self::expand_progress_bar(&package.name, &mutex);
cargo_thread.join().unwrap()?;
}
let idl_programs = shared_mutex.lock().unwrap().to_vec();
let codes_libs_pairs = shared_mutex_fuzzer.lock().unwrap().to_vec();

if idl_programs.is_empty() {
throw!(Error::NoProgramsFound);
} else {
(
Idl {
programs: idl_programs,
},
codes_libs_pairs,
)
}
let programs_data = shared_mutex_data.lock().unwrap().to_vec();
programs_data
}
/// Executes a cargo command to expand the Rust source code of a specified package.
///
Expand Down
54 changes: 30 additions & 24 deletions crates/client/src/fuzzer/fuzzer_generator.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
use std::collections::HashMap;

use crate::idl::Idl;
use crate::test_generator::ProgramData;
use proc_macro2::Ident;
use quote::{format_ident, ToTokens};
use syn::{parse_quote, parse_str};

/// Generates `fuzz_instructions.rs` from [Idl] created from Anchor programs.
pub fn generate_source_code(idl: &Idl) -> String {
let code = idl
.programs
pub fn generate_source_code(programs_data: &[ProgramData]) -> String {
let code = programs_data
.iter()
.map(|idl_program| {
let program_name = &idl_program.name.snake_case;
.map(|program_data| {
let program_name = &program_data.program_idl.name.snake_case;
let fuzz_instructions_module_name = format_ident!("{}_fuzz_instructions", program_name);
let module_name: syn::Ident = parse_str(program_name).unwrap();

let instructions = idl_program
let instructions = program_data
.program_idl
.instruction_account_pairs
.iter()
.fold(
Expand All @@ -34,7 +34,8 @@ pub fn generate_source_code(idl: &Idl) -> String {
)
.into_iter();

let instructions_data = idl_program
let instructions_data = program_data
.program_idl
.instruction_account_pairs
.iter()
.fold(
Expand Down Expand Up @@ -110,7 +111,8 @@ pub fn generate_source_code(idl: &Idl) -> String {
)
.into_iter();

let instructions_ixops_impls = idl_program
let instructions_ixops_impls = program_data
.program_idl
.instruction_account_pairs
.iter()
.fold(
Expand Down Expand Up @@ -184,21 +186,25 @@ pub fn generate_source_code(idl: &Idl) -> String {
)
.into_iter();

let fuzz_accounts = idl_program.instruction_account_pairs.iter().fold(
HashMap::new(),
|mut fuzz_accounts: HashMap<Ident, String>,
(_idl_instruction, idl_account_group)| {
idl_account_group.accounts.iter().fold(
&mut fuzz_accounts,
|fuzz_accounts, (name, _ty)| {
let name = format_ident!("{name}");
fuzz_accounts.entry(name).or_default();
fuzz_accounts
},
);
fuzz_accounts
},
);
let fuzz_accounts = program_data
.program_idl
.instruction_account_pairs
.iter()
.fold(
HashMap::new(),
|mut fuzz_accounts: HashMap<Ident, String>,
(_idl_instruction, idl_account_group)| {
idl_account_group.accounts.iter().fold(
&mut fuzz_accounts,
|fuzz_accounts, (name, _ty)| {
let name = format_ident!("{name}");
fuzz_accounts.entry(name).or_default();
fuzz_accounts
},
);
fuzz_accounts
},
);

// this ensures that the order of accounts is deterministic
// so we can use expected generated template within tests
Expand Down
Loading

0 comments on commit 1362660

Please sign in to comment.