#![allow(dead_code)] #![allow(unexpected_cfgs)] use anchor_lang::solana_program::account_info::{Account as AccountTrait, AccountInfo}; use anchor_lang::solana_program::hash::Hash; use arbitrary::Arbitrary; use arbitrary::Unstructured; use solana_sdk::account::Account; use solana_sdk::instruction::AccountMeta; use std::cell::RefCell; use std::collections::HashMap; use std::error::Error; use std::fmt::Display; use crate::config::Config; use crate::error::*; use crate::fuzz_client::FuzzClient; use crate::fuzz_test_executor::FuzzTestExecutor; pub struct FuzzData { pub pre_ixs: Vec, pub ixs: Vec, pub post_ixs: Vec, pub accounts: RefCell, } pub struct FuzzDataIterator<'a, T> { pre_ixs_iter: std::slice::Iter<'a, T>, ixs_iter: std::slice::Iter<'a, T>, post_ixs_iter: std::slice::Iter<'a, T>, } impl FuzzData { pub fn iter(&self) -> FuzzDataIterator<'_, T> { FuzzDataIterator { pre_ixs_iter: self.pre_ixs.iter(), ixs_iter: self.ixs.iter(), post_ixs_iter: self.post_ixs.iter(), } } } impl<'a, T> Iterator for FuzzDataIterator<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { self.pre_ixs_iter .next() .or_else(|| self.ixs_iter.next()) .or_else(|| self.post_ixs_iter.next()) } } impl FuzzData where T: FuzzTestExecutor + Display, { pub fn run_with_runtime( &self, client: &mut impl FuzzClient, config: &Config, ) -> core::result::Result<(), Box> { // solana_logger::setup_with_default("off"); // #[cfg(fuzzing_debug)] // solana_logger::setup_with_default( // "solana_rbpf::vm=debug,\ // solana_runtime::message_processor=debug,\ // solana_runtime::system_instruction_processor=trace,\ // solana_program_test=info,\ // fuzz_target=info", // ); #[cfg(feature = "fuzzing_debug")] { 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(feature = "fuzzing_debug")] eprintln!("\x1b[34mCurrently processing\x1b[0m: {}", fuzz_ix); if fuzz_ix .run_fuzzer(&self.accounts, client, &mut sent_txs, config) .is_err() { // for now skip following instructions in case of error and move to the next fuzz iteration return Ok(()); } } Ok(()) } } #[allow(unused_variables)] pub trait FuzzDataBuilder Arbitrary<'a>> { /// The instruction(s) executed as first, can be used for initialization. fn pre_ixs(u: &mut Unstructured) -> arbitrary::Result> { Ok(vec![]) } /// The main instructions for fuzzing. fn ixs(u: &mut Unstructured) -> arbitrary::Result> { let v = >::arbitrary(u)?; // Return always a vector with at least one element, othewise return error. if v.is_empty() { return Err(arbitrary::Error::NotEnoughData); } Ok(v) } /// The instuction(s) executed as last. fn post_ixs(u: &mut Unstructured) -> arbitrary::Result> { Ok(vec![]) } } pub fn build_ix_fuzz_data Arbitrary<'a>, T: FuzzDataBuilder, V: Default>( _data_builder: T, u: &mut arbitrary::Unstructured, ) -> arbitrary::Result> { Ok(FuzzData { pre_ixs: T::pre_ixs(u)?, ixs: T::ixs(u)?, post_ixs: T::post_ixs(u)?, accounts: RefCell::new(V::default()), }) } /// Creates `AccountInfo`s from `Accounts` and corresponding `AccountMeta` slices. pub fn get_account_infos_option<'info>( accounts: &'info mut [Option], metas: &'info [AccountMeta], ) -> Result>>, FuzzingError> { let iter = accounts.iter_mut().zip(metas); let r = iter .map(|(account, meta)| { if let Some(account) = account { let (lamports, data, owner, executable, rent_epoch) = account.get(); Some(AccountInfo::new( &meta.pubkey, meta.is_signer, meta.is_writable, lamports, data, owner, executable, rent_epoch, )) } else { None } }) .collect(); Ok(r) }