diff --git a/CHANGELOG.md b/CHANGELOG.md index b1a92a12..29c9fbfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ incremented upon a breaking change and the patch version will be incremented for ## [Unreleased] ### Added +- fix/allow to process duplicate transactions ([#147](https://github.com/Ackee-Blockchain/trident/pull/147)) - feat/possibility to implement custom transaction error handling ([#145](https://github.com/Ackee-Blockchain/trident/pull/145)) - feat/support of automatically obtaining fully qualified paths of Data Accounts Custom types for `accounts_snapshots.rs` ([#141](https://github.com/Ackee-Blockchain/trident/pull/141)) - feat/allow direct accounts manipulation and storage ([#142](https://github.com/Ackee-Blockchain/trident/pull/142)) diff --git a/crates/client/derive/fuzz_test_executor/src/lib.rs b/crates/client/derive/fuzz_test_executor/src/lib.rs index e7e23bfc..22298327 100644 --- a/crates/client/derive/fuzz_test_executor/src/lib.rs +++ b/crates/client/derive/fuzz_test_executor/src/lib.rs @@ -37,29 +37,40 @@ pub fn fuzz_test_executor(input: TokenStream) -> TokenStream { let sig: Vec<&Keypair> = signers.iter().collect(); transaction.sign(&sig, client.get_last_blockhash()); - let tx_result = client.process_transaction(transaction) - .map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))); + let duplicate_tx = if cfg!(allow_duplicate_txs) { + None + } else { + let message_hash = transaction.message().hash(); + sent_txs.insert(message_hash, ()) + }; + + match duplicate_tx { + Some(_) => eprintln!("\x1b[1;93mWarning\x1b[0m: Skipping duplicate instruction `{}`", self.to_context_string()), + None => { + let tx_result = client.process_transaction(transaction) + .map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))); - match tx_result { - Ok(_) => { - snaphot.capture_after(client).unwrap(); - let (acc_before, acc_after) = snaphot.get_snapshot() - .map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))) - .expect("Snapshot deserialization expect"); // we want to panic if we cannot unwrap to cause a crash + match tx_result { + Ok(_) => { + snaphot.capture_after(client).unwrap(); + let (acc_before, acc_after) = snaphot.get_snapshot() + .map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))) + .expect("Snapshot deserialization expect"); // we want to panic if we cannot unwrap to cause a crash - if let Err(e) = ix.check(acc_before, acc_after, data).map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))) { - eprintln!( - "CRASH DETECTED! Custom check after the {} instruction did not pass!", - self.to_context_string()); - panic!("{}", e) + if let Err(e) = ix.check(acc_before, acc_after, data).map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))) { + eprintln!( + "\x1b[31mCRASH DETECTED!\x1b[0m Custom check after the {} instruction did not pass!", + self.to_context_string()); + panic!("{}", e) + } + }, + Err(e) => { + let mut raw_accounts = snaphot.get_raw_pre_ix_accounts(); + ix.tx_error_handler(e, data, &mut raw_accounts)? + } } - }, - Err(e) => { - let mut raw_accounts = snaphot.get_raw_pre_ix_accounts(); - ix.tx_error_handler(e, data, &mut raw_accounts)? } } - } } }); @@ -71,6 +82,7 @@ pub fn fuzz_test_executor(input: TokenStream) -> TokenStream { program_id: Pubkey, accounts: &RefCell, client: &mut impl FuzzClient, + sent_txs: &mut HashMap, ) -> core::result::Result<(), FuzzClientErrorWithOrigin> { match self { #(#display_match_arms)* diff --git a/crates/client/src/commander.rs b/crates/client/src/commander.rs index 3164d05b..8c9a970b 100644 --- a/crates/client/src/commander.rs +++ b/crates/client/src/commander.rs @@ -153,7 +153,7 @@ impl Commander { // arguments so we need to parse the variable content. let hfuzz_run_args = std::env::var("HFUZZ_RUN_ARGS").unwrap_or_default(); - let fuzz_args = config.get_fuzz_args(hfuzz_run_args); + let fuzz_args = config.get_honggfuzz_args(hfuzz_run_args); // let cargo_target_dir = std::env::var("CARGO_TARGET_DIR").unwrap_or_default(); @@ -181,10 +181,20 @@ impl Commander { } } + let mut rustflags = if config.fuzz.allow_duplicate_txs { + "--cfg allow_duplicate_txs " + } else { + "" + } + .to_string(); + + rustflags.push_str(&std::env::var("RUSTFLAGS").unwrap_or_default()); + let mut child = Command::new("cargo") .env("HFUZZ_RUN_ARGS", fuzz_args) .env("CARGO_TARGET_DIR", cargo_target_dir) .env("HFUZZ_WORKSPACE", hfuzz_workspace) + .env("RUSTFLAGS", rustflags) .arg("hfuzz") .arg("run") .arg(target) @@ -226,12 +236,22 @@ impl Commander { let hfuzz_workspace = std::env::var("HFUZZ_WORKSPACE") .unwrap_or_else(|_| config.get_env_arg("HFUZZ_WORKSPACE")); - let fuzz_args = config.get_fuzz_args(hfuzz_run_args); + let fuzz_args = config.get_honggfuzz_args(hfuzz_run_args); + + let mut rustflags = if config.fuzz.allow_duplicate_txs { + "--cfg allow_duplicate_txs " + } else { + "" + } + .to_string(); + + rustflags.push_str(&std::env::var("RUSTFLAGS").unwrap_or_default()); let mut child = Command::new("cargo") .env("HFUZZ_RUN_ARGS", fuzz_args) .env("CARGO_TARGET_DIR", cargo_target_dir) .env("HFUZZ_WORKSPACE", hfuzz_workspace) + .env("RUSTFLAGS", rustflags) .arg("hfuzz") .arg("run") .arg(target) @@ -266,9 +286,19 @@ impl Commander { let cargo_target_dir = std::env::var("CARGO_TARGET_DIR") .unwrap_or_else(|_| config.get_env_arg("CARGO_TARGET_DIR")); + let mut rustflags = if config.fuzz.allow_duplicate_txs { + "--cfg allow_duplicate_txs " + } else { + "" + } + .to_string(); + + rustflags.push_str(&std::env::var("RUSTFLAGS").unwrap_or_default()); + // using exec rather than spawn and replacing current process to avoid unflushed terminal output after ctrl+c signal std::process::Command::new("cargo") .env("CARGO_TARGET_DIR", cargo_target_dir) + .env("RUSTFLAGS", rustflags) .arg("hfuzz") .arg("run-debug") .arg(target) diff --git a/crates/client/src/config.rs b/crates/client/src/config.rs index e31fd376..20ece1fd 100644 --- a/crates/client/src/config.rs +++ b/crates/client/src/config.rs @@ -41,19 +41,36 @@ impl From<_Test> for Test { } } } + +#[derive(Debug, Deserialize, Clone, Default)] +pub struct Fuzz { + pub allow_duplicate_txs: bool, +} +#[derive(Default, Debug, Deserialize, Clone)] +struct _Fuzz { + #[serde(default)] + pub allow_duplicate_txs: Option, +} +impl From<_Fuzz> for Fuzz { + fn from(_t: _Fuzz) -> Self { + Self { + allow_duplicate_txs: _t.allow_duplicate_txs.unwrap_or(false), + } + } +} #[derive(Debug, Deserialize, Clone)] -pub struct FuzzArg { +pub struct HonggFuzzArg { pub short_opt: Option, pub long_opt: Option, pub val: Option, } #[derive(Debug, Deserialize, Clone)] -pub struct Fuzz { - pub fuzz_args: Vec, +pub struct HonggFuzz { + pub fuzz_args: Vec, pub env_variables: HashMap, } #[derive(Default, Debug, Deserialize, Clone)] -struct _Fuzz { +struct _HonggFuzz { #[serde(default)] /// Timeout in seconds (default: 10) /// -t @@ -121,8 +138,8 @@ struct _Fuzz { pub save_all: Option, } -impl From<_Fuzz> for Fuzz { - fn from(_f: _Fuzz) -> Self { +impl From<_HonggFuzz> for HonggFuzz { + fn from(_f: _HonggFuzz) -> Self { let mut _self = Self { fuzz_args: vec![], env_variables: HashMap::default(), @@ -132,20 +149,22 @@ impl From<_Fuzz> for Fuzz { let timeout = _f.timeout.unwrap_or(10); _self .fuzz_args - .push(FuzzArg::new("-t", "--timeout", &timeout.to_string())); + .push(HonggFuzzArg::new("-t", "--timeout", &timeout.to_string())); // iterations let iterations = _f.iterations.unwrap_or(0); - _self - .fuzz_args - .push(FuzzArg::new("-N", "--iterations", &iterations.to_string())); + _self.fuzz_args.push(HonggFuzzArg::new( + "-N", + "--iterations", + &iterations.to_string(), + )); // threads let threads = _f.threads.unwrap_or(0); if threads > 0 { _self .fuzz_args - .push(FuzzArg::new("-n", "--threads", &threads.to_string())); + .push(HonggFuzzArg::new("-n", "--threads", &threads.to_string())); } // keep_output @@ -153,12 +172,14 @@ impl From<_Fuzz> for Fuzz { if keep_output { _self .fuzz_args - .push(FuzzArg::new("-Q", "--keep_output", "")); + .push(HonggFuzzArg::new("-Q", "--keep_output", "")); } // verbose let verbose = _f.verbose.unwrap_or(false); if verbose { - _self.fuzz_args.push(FuzzArg::new("-v", "--verbose", "")); + _self + .fuzz_args + .push(HonggFuzzArg::new("-v", "--verbose", "")); } // exit_upon_crash @@ -166,11 +187,11 @@ impl From<_Fuzz> for Fuzz { if exit_upon_crash { _self .fuzz_args - .push(FuzzArg::new("", "--exit_upon_crash", "")); + .push(HonggFuzzArg::new("", "--exit_upon_crash", "")); } // mutations_per_run let mutations_per_run = _f.mutations_per_run.unwrap_or(6); - _self.fuzz_args.push(FuzzArg::new( + _self.fuzz_args.push(HonggFuzzArg::new( "-r", "--mutations_per_run", &mutations_per_run.to_string(), @@ -204,24 +225,24 @@ impl From<_Fuzz> for Fuzz { if !crash_dir.is_empty() { _self .fuzz_args - .push(FuzzArg::new("", "--crashdir", &crash_dir)); + .push(HonggFuzzArg::new("", "--crashdir", &crash_dir)); } // extension let extension = _f.extension.unwrap_or_default(); if !extension.is_empty() { _self .fuzz_args - .push(FuzzArg::new("-e", "--extension", &extension)); + .push(HonggFuzzArg::new("-e", "--extension", &extension)); } // run_time let run_time = _f.run_time.unwrap_or(0); _self .fuzz_args - .push(FuzzArg::new("", "--run_time", &run_time.to_string())); + .push(HonggFuzzArg::new("", "--run_time", &run_time.to_string())); // max_file_size let max_file_size = _f.max_file_size.unwrap_or(1_048_576); - _self.fuzz_args.push(FuzzArg::new( + _self.fuzz_args.push(HonggFuzzArg::new( "-F", "--max_file_size", &max_file_size.to_string(), @@ -229,13 +250,15 @@ impl From<_Fuzz> for Fuzz { // save_all let save_all = _f.save_all.unwrap_or(false); if save_all { - _self.fuzz_args.push(FuzzArg::new("-u", "--save_all", "")); + _self + .fuzz_args + .push(HonggFuzzArg::new("-u", "--save_all", "")); } _self } } -impl FuzzArg { +impl HonggFuzzArg { fn new(short_opt: &str, long_opt: &str, val: &str) -> Self { let short_opt = if short_opt.is_empty() { None @@ -263,6 +286,7 @@ impl FuzzArg { #[derive(Debug, Deserialize, Clone)] pub struct Config { pub test: Test, + pub honggfuzz: HonggFuzz, pub fuzz: Fuzz, } @@ -271,6 +295,8 @@ struct _Config { #[serde(default)] pub test: Option<_Test>, #[serde(default)] + pub honggfuzz: Option<_HonggFuzz>, + #[serde(default)] pub fuzz: Option<_Fuzz>, } @@ -278,6 +304,7 @@ impl From<_Config> for Config { fn from(_c: _Config) -> Self { Self { test: _c.test.unwrap_or_default().into(), + honggfuzz: _c.honggfuzz.unwrap_or_default().into(), fuzz: _c.fuzz.unwrap_or_default().into(), } } @@ -316,14 +343,14 @@ impl Config { } throw!(Error::BadWorkspace) } - pub fn get_fuzz_args(&self, cli_input: String) -> String { + pub fn get_honggfuzz_args(&self, cli_input: String) -> String { // Tested on a few examples, HFUZZ_RUN_ARGS give precedence to the later arguments. // so if HFUZZ_RUN_ARGS="-t 10 -t 15" -> timeout 15s is applied. // That means we do not need to parse the arguments from the CLI; // thus, we can simply append them at the end, and the CLI will have precedence. let mut args: Vec = self - .fuzz + .honggfuzz .fuzz_args .iter() .map(|a| { @@ -342,7 +369,7 @@ impl Config { } pub fn get_env_arg(&self, env_variable: &str) -> String { let expect = format!("{env_variable} not found"); - self.fuzz + self.honggfuzz .env_variables .get(env_variable) .expect(&expect) @@ -352,7 +379,7 @@ impl Config { #[cfg(test)] mod tests { - impl Default for Fuzz { + impl Default for HonggFuzz { fn default() -> Self { let mut env_variables: HashMap = HashMap::default(); env_variables.insert( @@ -365,12 +392,12 @@ mod tests { ); Self { fuzz_args: vec![ - FuzzArg::new("-t", "--timeout", &10.to_string()), - FuzzArg::new("-N", "--iterations", &0.to_string()), - FuzzArg::new("-r", "--mutations_per_run", &6.to_string()), - FuzzArg::new("-e", "--extension", "fuzz"), - FuzzArg::new("", "--run_time", &0.to_string()), - FuzzArg::new("-F", "--max_file_size", &1_048_576.to_string()), + HonggFuzzArg::new("-t", "--timeout", &10.to_string()), + HonggFuzzArg::new("-N", "--iterations", &0.to_string()), + HonggFuzzArg::new("-r", "--mutations_per_run", &6.to_string()), + HonggFuzzArg::new("-e", "--extension", "fuzz"), + HonggFuzzArg::new("", "--run_time", &0.to_string()), + HonggFuzzArg::new("-F", "--max_file_size", &1_048_576.to_string()), ], env_variables, } @@ -382,10 +409,11 @@ mod tests { fn test_merge_and_precedence1() { let config = Config { test: Test::default(), + honggfuzz: HonggFuzz::default(), fuzz: Fuzz::default(), }; - let env_var_string = config.get_fuzz_args(String::default()); + let env_var_string = config.get_honggfuzz_args(String::default()); assert_eq!( env_var_string, "-t 10 -N 0 -r 6 -e fuzz --run_time 0 -F 1048576 " @@ -395,10 +423,11 @@ mod tests { fn test_merge_and_precedence2() { let config = Config { test: Test::default(), + honggfuzz: HonggFuzz::default(), fuzz: Fuzz::default(), }; - let env_var_string = config.get_fuzz_args("-t 0 -N10 --exit_upon_crash".to_string()); + let env_var_string = config.get_honggfuzz_args("-t 0 -N10 --exit_upon_crash".to_string()); assert_eq!( env_var_string, @@ -409,10 +438,11 @@ mod tests { fn test_merge_and_precedence3() { let config = Config { test: Test::default(), + honggfuzz: HonggFuzz::default(), fuzz: Fuzz::default(), }; let env_var_string = - config.get_fuzz_args("-t 100 -N 5000 -Q -v --exit_upon_crash".to_string()); + config.get_honggfuzz_args("-t 100 -N 5000 -Q -v --exit_upon_crash".to_string()); assert_eq!( env_var_string, "-t 10 -N 0 -r 6 -e fuzz --run_time 0 -F 1048576 -t 100 -N 5000 -Q -v --exit_upon_crash" @@ -422,10 +452,11 @@ mod tests { fn test_merge_and_precedence4() { let config = Config { test: Test::default(), + honggfuzz: HonggFuzz::default(), fuzz: Fuzz::default(), }; - let env_var_string = config.get_fuzz_args("-t 10 -N 500 -Q -v --exit_upon_crash -n 15 --mutations_per_run 8 --verifier -W random_dir --crashdir random_dir5 --run_time 666".to_string()); + let env_var_string = config.get_honggfuzz_args("-t 10 -N 500 -Q -v --exit_upon_crash -n 15 --mutations_per_run 8 --verifier -W random_dir --crashdir random_dir5 --run_time 666".to_string()); assert_eq!( env_var_string, "-t 10 -N 0 -r 6 -e fuzz --run_time 0 -F 1048576 -t 10 -N 500 -Q -v --exit_upon_crash -n 15 --mutations_per_run 8 --verifier -W random_dir --crashdir random_dir5 --run_time 666" @@ -435,10 +466,11 @@ mod tests { fn test_merge_and_precedence5() { let config = Config { test: Test::default(), + honggfuzz: HonggFuzz::default(), fuzz: Fuzz::default(), }; - let env_var_string = config.get_fuzz_args("-t 10 -N 500 -Q -v --exit_upon_crash -n 15 --verifier -W random_dir --crashdir random_dir5 --run_time 666".to_string()); + let env_var_string = config.get_honggfuzz_args("-t 10 -N 500 -Q -v --exit_upon_crash -n 15 --verifier -W random_dir --crashdir random_dir5 --run_time 666".to_string()); assert_eq!( env_var_string, "-t 10 -N 0 -r 6 -e fuzz --run_time 0 -F 1048576 -t 10 -N 500 -Q -v --exit_upon_crash -n 15 --verifier -W random_dir --crashdir random_dir5 --run_time 666" @@ -448,6 +480,7 @@ mod tests { fn test_obtain_env_variables() { let config = Config { test: Test::default(), + honggfuzz: HonggFuzz::default(), fuzz: Fuzz::default(), }; @@ -461,16 +494,17 @@ mod tests { fn test_obtain_env_variables2() { let mut config = Config { test: Test::default(), + honggfuzz: HonggFuzz::default(), fuzz: Fuzz::default(), }; config - .fuzz + .honggfuzz .env_variables .insert(CARGO_TARGET_DIR_ENV.to_owned(), "new_value_x".to_owned()); config - .fuzz + .honggfuzz .env_variables .insert(HFUZZ_WORKSPACE_ENV.to_owned(), "new_value_y".to_owned()); diff --git a/crates/client/src/fuzzer/data_builder.rs b/crates/client/src/fuzzer/data_builder.rs index 9b751974..174611dc 100644 --- a/crates/client/src/fuzzer/data_builder.rs +++ b/crates/client/src/fuzzer/data_builder.rs @@ -9,6 +9,7 @@ use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Keypair; use solana_sdk::transaction::VersionedTransaction; use std::cell::RefCell; +use std::collections::HashMap; use std::error::Error; use std::fmt::Display; @@ -69,19 +70,21 @@ where #[cfg(fuzzing_debug)] { - eprintln!("Instructions sequence:"); + eprintln!("\x1b[34mInstructions sequence\x1b[0m:"); for ix in self.iter() { eprintln!("{}", ix); } eprintln!("------ End of Instructions sequence ------ "); } + let mut sent_txs: HashMap = HashMap::new(); + for fuzz_ix in &mut self.iter() { #[cfg(fuzzing_debug)] - eprintln!("Currently processing: {}", fuzz_ix); + eprintln!("\x1b[34mCurrently processing\x1b[0m: {}", fuzz_ix); if fuzz_ix - .run_fuzzer(program_id, &self.accounts, client) + .run_fuzzer(program_id, &self.accounts, client, &mut sent_txs) .is_err() { // for now skip following instructions in case of error and move to the next fuzz iteration @@ -98,6 +101,7 @@ pub trait FuzzTestExecutor { program_id: Pubkey, accounts: &RefCell, client: &mut impl FuzzClient, + sent_txs: &mut HashMap, ) -> core::result::Result<(), FuzzClientErrorWithOrigin>; } diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 63c78078..d2fb699e 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -47,6 +47,7 @@ pub mod fuzzing { pub use super::fuzzer::snapshot::Snapshot; pub use super::fuzzer::*; pub use std::cell::RefCell; + pub use std::collections::HashMap; pub use trident_derive_displayix::DisplayIx; pub use trident_derive_fuzz_deserialize::FuzzDeserialize; pub use trident_derive_fuzz_test_executor::FuzzTestExecutor; diff --git a/crates/client/src/templates/Trident.toml.tmpl b/crates/client/src/templates/Trident.toml.tmpl index f0ced763..e05d7801 100644 --- a/crates/client/src/templates/Trident.toml.tmpl +++ b/crates/client/src/templates/Trident.toml.tmpl @@ -2,7 +2,7 @@ validator_startup_timeout = 15000 -[fuzz] +[honggfuzz] # Timeout in seconds (default: 10) timeout = 10 # Number of fuzzing iterations (default: 0 [no limit]) @@ -32,3 +32,7 @@ run_time = 0 max_file_size = 1048576 # Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false) save_all = false + +[fuzz] +# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false) +allow_duplicate_txs = false diff --git a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs b/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs index c9d3f407..ea121bee 100644 --- a/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs +++ b/crates/client/tests/test_data/fuzzer_macros/fuzz_fuzz_test_executor.expanded.rs @@ -1,148 +1,146 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::rust_2018::*; +#[macro_use] +extern crate std; use trident_client::FuzzTestExecutor; pub enum FuzzInstruction { InitVesting(InitVesting), WithdrawUnlocked(WithdrawUnlocked), } impl FuzzTestExecutor for FuzzInstruction { - fn run_fuzzer( - &self, - program_id: Pubkey, - accounts: &RefCell, - client: &mut impl FuzzClient, - ) -> core::result::Result<(), FuzzClientErrorWithOrigin> { + fn run_fuzzer(&self, program_id: Pubkey, accounts: &RefCell, + client: &mut impl FuzzClient, sent_txs: &mut HashMap) + -> core::result::Result<(), FuzzClientErrorWithOrigin> { match self { FuzzInstruction::InitVesting(ix) => { - let (mut signers, metas) = ix - .get_accounts(client, &mut accounts.borrow_mut()) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Accounts calculation expect"); + let (mut signers, metas) = + ix.get_accounts(client, + &mut accounts.borrow_mut()).map_err(|e| + e.with_origin(Origin::Instruction(self.to_context_string()))).expect("Accounts calculation expect"); let mut snaphot = Snapshot::new(&metas, ix); snaphot.capture_before(client).unwrap(); - let data = ix - .get_data(client, &mut accounts.borrow_mut()) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Data calculation expect"); - let ixx = Instruction { - program_id, - accounts: metas.clone(), - data: data.data(), - }; - let mut transaction = Transaction::new_with_payer( - &[ixx], - Some(&client.payer().pubkey()), - ); + let data = + ix.get_data(client, + &mut accounts.borrow_mut()).map_err(|e| + e.with_origin(Origin::Instruction(self.to_context_string()))).expect("Data calculation expect"); + let ixx = + Instruction { + program_id, + accounts: metas.clone(), + data: data.data(), + }; + let mut transaction = + Transaction::new_with_payer(&[ixx], + Some(&client.payer().pubkey())); signers.push(client.payer().clone()); let sig: Vec<&Keypair> = signers.iter().collect(); transaction.sign(&sig, client.get_last_blockhash()); - let tx_result = client - .process_transaction(transaction) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }); - match tx_result { - Ok(_) => { - snaphot.capture_after(client).unwrap(); - let (acc_before, acc_after) = snaphot - .get_snapshot() - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Snapshot deserialization expect"); - if let Err(e) - = ix - .check(acc_before, acc_after, data) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - { - { - ::std::io::_eprint( - format_args!( - "CRASH DETECTED! Custom check after the {0} instruction did not pass!\n", - self.to_context_string(), - ), - ); - }; - { - ::core::panicking::panic_display(&e); + let duplicate_tx = + if false { + None + } else { + let message_hash = transaction.message().hash(); + sent_txs.insert(message_hash, ()) + }; + match duplicate_tx { + Some(_) => { + ::std::io::_eprint(format_args!("\u{{1b}}[1;93mWarning\u{{1b}}[0m: Skipping duplicate instruction `{0}`\n", + self.to_context_string())); + } + None => { + let tx_result = + client.process_transaction(transaction).map_err(|e| + e.with_origin(Origin::Instruction(self.to_context_string()))); + match tx_result { + Ok(_) => { + snaphot.capture_after(client).unwrap(); + let (acc_before, acc_after) = + snaphot.get_snapshot().map_err(|e| + e.with_origin(Origin::Instruction(self.to_context_string()))).expect("Snapshot deserialization expect"); + if let Err(e) = + ix.check(acc_before, acc_after, + data).map_err(|e| + e.with_origin(Origin::Instruction(self.to_context_string()))) + { + { + ::std::io::_eprint(format_args!("\u{{1b}}[31mCRASH DETECTED!\u{{1b}}[0m Custom check after the {0} instruction did not pass!\n", + self.to_context_string())); + }; + { ::core::panicking::panic_display(&e); } + } + } + Err(e) => { + let mut raw_accounts = snaphot.get_raw_pre_ix_accounts(); + ix.tx_error_handler(e, data, &mut raw_accounts)? } } } - Err(e) => { - let mut raw_accounts = snaphot.get_raw_pre_ix_accounts(); - ix.tx_error_handler(e, data, &mut raw_accounts)? - } } } FuzzInstruction::WithdrawUnlocked(ix) => { - let (mut signers, metas) = ix - .get_accounts(client, &mut accounts.borrow_mut()) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Accounts calculation expect"); + let (mut signers, metas) = + ix.get_accounts(client, + &mut accounts.borrow_mut()).map_err(|e| + e.with_origin(Origin::Instruction(self.to_context_string()))).expect("Accounts calculation expect"); let mut snaphot = Snapshot::new(&metas, ix); snaphot.capture_before(client).unwrap(); - let data = ix - .get_data(client, &mut accounts.borrow_mut()) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Data calculation expect"); - let ixx = Instruction { - program_id, - accounts: metas.clone(), - data: data.data(), - }; - let mut transaction = Transaction::new_with_payer( - &[ixx], - Some(&client.payer().pubkey()), - ); + let data = + ix.get_data(client, + &mut accounts.borrow_mut()).map_err(|e| + e.with_origin(Origin::Instruction(self.to_context_string()))).expect("Data calculation expect"); + let ixx = + Instruction { + program_id, + accounts: metas.clone(), + data: data.data(), + }; + let mut transaction = + Transaction::new_with_payer(&[ixx], + Some(&client.payer().pubkey())); signers.push(client.payer().clone()); let sig: Vec<&Keypair> = signers.iter().collect(); transaction.sign(&sig, client.get_last_blockhash()); - let tx_result = client - .process_transaction(transaction) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }); - match tx_result { - Ok(_) => { - snaphot.capture_after(client).unwrap(); - let (acc_before, acc_after) = snaphot - .get_snapshot() - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - .expect("Snapshot deserialization expect"); - if let Err(e) - = ix - .check(acc_before, acc_after, data) - .map_err(|e| { - e.with_origin(Origin::Instruction(self.to_context_string())) - }) - { - { - ::std::io::_eprint( - format_args!( - "CRASH DETECTED! Custom check after the {0} instruction did not pass!\n", - self.to_context_string(), - ), - ); - }; - { - ::core::panicking::panic_display(&e); + let duplicate_tx = + if false { + None + } else { + let message_hash = transaction.message().hash(); + sent_txs.insert(message_hash, ()) + }; + match duplicate_tx { + Some(_) => { + ::std::io::_eprint(format_args!("\u{{1b}}[1;93mWarning\u{{1b}}[0m: Skipping duplicate instruction `{0}`\n", + self.to_context_string())); + } + None => { + let tx_result = + client.process_transaction(transaction).map_err(|e| + e.with_origin(Origin::Instruction(self.to_context_string()))); + match tx_result { + Ok(_) => { + snaphot.capture_after(client).unwrap(); + let (acc_before, acc_after) = + snaphot.get_snapshot().map_err(|e| + e.with_origin(Origin::Instruction(self.to_context_string()))).expect("Snapshot deserialization expect"); + if let Err(e) = + ix.check(acc_before, acc_after, + data).map_err(|e| + e.with_origin(Origin::Instruction(self.to_context_string()))) + { + { + ::std::io::_eprint(format_args!("\u{{1b}}[31mCRASH DETECTED!\u{{1b}}[0m Custom check after the {0} instruction did not pass!\n", + self.to_context_string())); + }; + { ::core::panicking::panic_display(&e); } + } + } + Err(e) => { + let mut raw_accounts = snaphot.get_raw_pre_ix_accounts(); + ix.tx_error_handler(e, data, &mut raw_accounts)? } } } - Err(e) => { - let mut raw_accounts = snaphot.get_raw_pre_ix_accounts(); - ix.tx_error_handler(e, data, &mut raw_accounts)? - } } } } diff --git a/examples/fuzz-tests/arbitrary-custom-types-4/Trident.toml b/examples/fuzz-tests/arbitrary-custom-types-4/Trident.toml index f0ced763..e05d7801 100644 --- a/examples/fuzz-tests/arbitrary-custom-types-4/Trident.toml +++ b/examples/fuzz-tests/arbitrary-custom-types-4/Trident.toml @@ -2,7 +2,7 @@ validator_startup_timeout = 15000 -[fuzz] +[honggfuzz] # Timeout in seconds (default: 10) timeout = 10 # Number of fuzzing iterations (default: 0 [no limit]) @@ -32,3 +32,7 @@ run_time = 0 max_file_size = 1048576 # Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false) save_all = false + +[fuzz] +# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false) +allow_duplicate_txs = false diff --git a/examples/fuzz-tests/arbitrary-limit-inputs-5/Trident.toml b/examples/fuzz-tests/arbitrary-limit-inputs-5/Trident.toml index f0ced763..e05d7801 100644 --- a/examples/fuzz-tests/arbitrary-limit-inputs-5/Trident.toml +++ b/examples/fuzz-tests/arbitrary-limit-inputs-5/Trident.toml @@ -2,7 +2,7 @@ validator_startup_timeout = 15000 -[fuzz] +[honggfuzz] # Timeout in seconds (default: 10) timeout = 10 # Number of fuzzing iterations (default: 0 [no limit]) @@ -32,3 +32,7 @@ run_time = 0 max_file_size = 1048576 # Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false) save_all = false + +[fuzz] +# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false) +allow_duplicate_txs = false diff --git a/examples/fuzz-tests/hello_world/Trident.toml b/examples/fuzz-tests/hello_world/Trident.toml index 5d8f621d..6c05d2e0 100644 --- a/examples/fuzz-tests/hello_world/Trident.toml +++ b/examples/fuzz-tests/hello_world/Trident.toml @@ -2,7 +2,7 @@ validator_startup_timeout = 15000 -[fuzz] +[honggfuzz] # Timeout in seconds (default: 10) timeout = 10 # Number of fuzzing iterations (default: 0 [no limit]) @@ -32,3 +32,7 @@ run_time = 0 max_file_size = 1048576 # Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false) save_all = false + +[fuzz] +# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false) +allow_duplicate_txs = false diff --git a/examples/fuzz-tests/incorrect-integer-arithmetic-3/Trident.toml b/examples/fuzz-tests/incorrect-integer-arithmetic-3/Trident.toml index f0ced763..e05d7801 100644 --- a/examples/fuzz-tests/incorrect-integer-arithmetic-3/Trident.toml +++ b/examples/fuzz-tests/incorrect-integer-arithmetic-3/Trident.toml @@ -2,7 +2,7 @@ validator_startup_timeout = 15000 -[fuzz] +[honggfuzz] # Timeout in seconds (default: 10) timeout = 10 # Number of fuzzing iterations (default: 0 [no limit]) @@ -32,3 +32,7 @@ run_time = 0 max_file_size = 1048576 # Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false) save_all = false + +[fuzz] +# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false) +allow_duplicate_txs = false diff --git a/examples/fuzz-tests/incorrect-ix-sequence-1/Trident.toml b/examples/fuzz-tests/incorrect-ix-sequence-1/Trident.toml index f0ced763..e05d7801 100644 --- a/examples/fuzz-tests/incorrect-ix-sequence-1/Trident.toml +++ b/examples/fuzz-tests/incorrect-ix-sequence-1/Trident.toml @@ -2,7 +2,7 @@ validator_startup_timeout = 15000 -[fuzz] +[honggfuzz] # Timeout in seconds (default: 10) timeout = 10 # Number of fuzzing iterations (default: 0 [no limit]) @@ -32,3 +32,7 @@ run_time = 0 max_file_size = 1048576 # Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false) save_all = false + +[fuzz] +# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false) +allow_duplicate_txs = false diff --git a/examples/fuzz-tests/unauthorized-access-2/Trident.toml b/examples/fuzz-tests/unauthorized-access-2/Trident.toml index f0ced763..e05d7801 100644 --- a/examples/fuzz-tests/unauthorized-access-2/Trident.toml +++ b/examples/fuzz-tests/unauthorized-access-2/Trident.toml @@ -2,7 +2,7 @@ validator_startup_timeout = 15000 -[fuzz] +[honggfuzz] # Timeout in seconds (default: 10) timeout = 10 # Number of fuzzing iterations (default: 0 [no limit]) @@ -32,3 +32,7 @@ run_time = 0 max_file_size = 1048576 # Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false) save_all = false + +[fuzz] +# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false) +allow_duplicate_txs = false diff --git a/examples/fuzz-tests/unchecked-arithmetic-0/Trident.toml b/examples/fuzz-tests/unchecked-arithmetic-0/Trident.toml index f0ced763..e05d7801 100644 --- a/examples/fuzz-tests/unchecked-arithmetic-0/Trident.toml +++ b/examples/fuzz-tests/unchecked-arithmetic-0/Trident.toml @@ -2,7 +2,7 @@ validator_startup_timeout = 15000 -[fuzz] +[honggfuzz] # Timeout in seconds (default: 10) timeout = 10 # Number of fuzzing iterations (default: 0 [no limit]) @@ -32,3 +32,7 @@ run_time = 0 max_file_size = 1048576 # Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false) save_all = false + +[fuzz] +# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false) +allow_duplicate_txs = false diff --git a/examples/integration-tests/escrow/Trident.toml b/examples/integration-tests/escrow/Trident.toml index f0ced763..e05d7801 100644 --- a/examples/integration-tests/escrow/Trident.toml +++ b/examples/integration-tests/escrow/Trident.toml @@ -2,7 +2,7 @@ validator_startup_timeout = 15000 -[fuzz] +[honggfuzz] # Timeout in seconds (default: 10) timeout = 10 # Number of fuzzing iterations (default: 0 [no limit]) @@ -32,3 +32,7 @@ run_time = 0 max_file_size = 1048576 # Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false) save_all = false + +[fuzz] +# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false) +allow_duplicate_txs = false diff --git a/examples/integration-tests/turnstile/Trident.toml b/examples/integration-tests/turnstile/Trident.toml index f0ced763..e05d7801 100644 --- a/examples/integration-tests/turnstile/Trident.toml +++ b/examples/integration-tests/turnstile/Trident.toml @@ -2,7 +2,7 @@ validator_startup_timeout = 15000 -[fuzz] +[honggfuzz] # Timeout in seconds (default: 10) timeout = 10 # Number of fuzzing iterations (default: 0 [no limit]) @@ -32,3 +32,7 @@ run_time = 0 max_file_size = 1048576 # Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false) save_all = false + +[fuzz] +# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false) +allow_duplicate_txs = false