Skip to content

Commit

Permalink
Debug-mode detailed output (#125)
Browse files Browse the repository at this point in the history
* 🚧 WIP: display deserialized instruction data within debug mode

* πŸ§‘β€πŸ’» show_account macro + docu

* πŸ§‘β€πŸ’» to_context_string() fn, debug output

* ♻️ Updated derive macros

* ♻️ Updated crash error message

* ♻️ pass tests, remove unnecessary macro

---------

Co-authored-by: lukacan <andrej@DESKTOP-UME2160.localdomain>
Co-authored-by: Ikrk <ikrk@centrum.cz>
  • Loading branch information
3 people committed Feb 20, 2024
1 parent a5b5afb commit a8d2529
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 91 deletions.
33 changes: 31 additions & 2 deletions crates/client/derive/display_ix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,33 @@ pub fn display_ix(input: TokenStream) -> TokenStream {

let display_impl = match &input.data {
Data::Enum(enum_data) => {
let to_context_string_match_arms = enum_data.variants.iter().map(|variant| {
let variant_name = &variant.ident;
quote! {
#enum_name::#variant_name (_) => String::from(stringify!(#variant_name)),
}
});
let display_match_arms = enum_data.variants.iter().map(|variant| {
let variant_name = &variant.ident;

quote! {
#enum_name::#variant_name (_) => write!(f, stringify!(#variant_name)),
match &variant.fields {
syn::Fields::Unnamed(fields) => {
if fields.unnamed.len() == 1 {
quote! {
#enum_name::#variant_name(ref content) => {
write!(f, stringify!(#variant_name))?;
write!(f, "({:#?})", content)
},
}
} else {
quote! {
#enum_name::#variant_name (_) => write!(f, stringify!(#variant_name)),
}
}
},
_ => quote! {
#enum_name::#variant_name => write!(f, stringify!(#variant_name)),
},
}
});

Expand All @@ -25,6 +47,13 @@ pub fn display_ix(input: TokenStream) -> TokenStream {
}
}
}
impl #enum_name {
fn to_context_string(&self)->String{
match self {
#(#to_context_string_match_arms)*
}
}
}
}
}
_ => panic!("DisplayIx can only be derived for enums"),
Expand Down
16 changes: 7 additions & 9 deletions crates/client/derive/fuzz_test_executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn fuzz_test_executor(input: TokenStream) -> TokenStream {
#enum_name::#variant_name (ix) => {
let (mut signers, metas) =
if let Ok(acc) = ix.get_accounts(client, &mut accounts.borrow_mut())
.map_err(|e| e.with_origin(Origin::Instruction(self.to_string()))) {
.map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))) {
acc
} else {
return Ok(());
Expand All @@ -25,7 +25,7 @@ pub fn fuzz_test_executor(input: TokenStream) -> TokenStream {
snaphot.capture_before(client).unwrap();
let data =
if let Ok(data) = ix.get_data(client, &mut accounts.borrow_mut())
.map_err(|e| e.with_origin(Origin::Instruction(self.to_string()))) {
.map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))) {
data
} else {
return Ok(());
Expand All @@ -42,19 +42,17 @@ pub fn fuzz_test_executor(input: TokenStream) -> TokenStream {
transaction.sign(&sig, client.get_last_blockhash());

let res = client.process_transaction(transaction)
.map_err(|e| e.with_origin(Origin::Instruction(self.to_string())));
.map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string())));

// this can return FuzzClientErrorWithOrigin
snaphot.capture_after(client).unwrap();
let (acc_before, acc_after) = snaphot.get_snapshot()
.map_err(|e| e.with_origin(Origin::Instruction(self.to_string()))).unwrap(); // we want to panic if we cannot unwrap to cause a crash
.map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))).unwrap(); // 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_string()))) {
if let Err(e) = ix.check(acc_before, acc_after, data).map_err(|e| e.with_origin(Origin::Instruction(self.to_context_string()))) {
eprintln!(
"Custom check after the {} instruction did not pass with the error message: {}",
self, e
);
eprintln!("Instruction data submitted to the instruction were:"); // TODO data does not implement Debug trait -> derive Debug trait on InitializeIx and automaticaly implement conversion from Initialize to InitializeIx
"CRASH DETECTED! Custom check after the {} instruction did not pass!",
self.to_context_string());
panic!("{}", e)
}

Expand Down
48 changes: 46 additions & 2 deletions crates/client/src/fuzzer/data_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,17 @@ where
// fuzz_target=info",
// );

// #[cfg(fuzzing_debug)]
#[cfg(fuzzing_debug)]
{
eprintln!("Instructions sequence:");
for ix in self.iter() {
eprintln!("{}", ix);
}
eprintln!("------ End of Instructions sequence ------ ");
}

for fuzz_ix in &mut self.iter() {
// #[cfg(fuzzing_debug)]
#[cfg(fuzzing_debug)]
eprintln!("Currently processing: {}", fuzz_ix);

fuzz_ix.run_fuzzer(program_id, &self.accounts, client)?;
Expand Down Expand Up @@ -212,6 +213,49 @@ macro_rules! fuzz_trd {
});
};
}
/// Prints the details of a given account in a pretty-printed format.
///
/// This macro takes a single argument, which is an expression referring to the account
/// you want to print. The account data structure must implement or derive the [`Debug`]
/// trait for this macro to work, as it relies on `std::fmt::Debug` for formatting.
///
/// # Examples
///
/// ```rust,ignore
/// use trdelnik_client::fuzzing::show_account;
///
/// #[derive(Debug)]
/// #[account]
/// struct Escrow {
/// recipeint: Pubkey,
/// id: u32,
/// balance: f64,
/// name: String,
/// }
///
/// fn check(
/// &self,
/// pre_ix: Self::IxSnapshot,
/// post_ix: Self::IxSnapshot,
/// ix_data: Self::IxData,
/// ) -> Result<(), FuzzingError> {
/// if let Some(escrow) = pre_ix.escrow{
/// show_account!(escrow);
/// }
/// }
/// ```
///
/// # Requirements
///
/// The `account` passed to `show_account!` must implement or derive the [`Debug`] trait.
/// Attempting to use this macro with a type that does not meet this requirement will
/// result in a compilation error.
#[macro_export]
macro_rules! show_account {
($account:expr) => {
eprintln!("{:#?}", $account);
};
}

pub fn build_ix_fuzz_data<U: for<'a> Arbitrary<'a>, T: FuzzDataBuilder<U>, V: Default>(
_data_builder: T,
Expand Down
8 changes: 4 additions & 4 deletions crates/client/src/fuzzer/fuzzer_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub fn generate_source_code(idl: &Idl) -> String {
.collect::<Vec<_>>();

let ix_enum_variant: syn::ItemStruct = parse_quote! {
#[derive(Arbitrary, Clone)]
#[derive(Arbitrary, Debug)]
pub struct #instruction_name {
pub accounts: #instruction_accounts_name,
pub data: #instruction_data_name
Expand All @@ -88,14 +88,14 @@ pub fn generate_source_code(idl: &Idl) -> String {
};

let ix_accounts: syn::ItemStruct = parse_quote! {
#[derive(Arbitrary, Clone)]
#[derive(Arbitrary, Debug)]
pub struct #instruction_accounts_name {
#(pub #accounts),*
}

};
let ix_data: syn::ItemStruct = parse_quote! {
#[derive(Arbitrary, Clone)]
#[derive(Arbitrary, Debug)]
pub struct #instruction_data_name {
#(pub #parameters),*
}
Expand Down Expand Up @@ -208,7 +208,7 @@ pub fn generate_source_code(idl: &Idl) -> String {
use trdelnik_client::fuzzing::*;
use crate::accounts_snapshots::*;

#[derive(Arbitrary, Clone, DisplayIx, FuzzTestExecutor, FuzzDeserialize)]
#[derive(Arbitrary, DisplayIx, FuzzTestExecutor, FuzzDeserialize)]
pub enum FuzzInstruction {
#(#instructions),*
}
Expand Down
4 changes: 2 additions & 2 deletions crates/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ pub mod fuzzing {
pub use super::{
anchor_lang, anchor_lang::system_program::ID as SYSTEM_PROGRAM_ID,
anchor_lang::InstructionData, anchor_lang::ToAccountInfo, anchor_lang::ToAccountMetas,
fuzz_trd, solana_sdk::account::Account, solana_sdk::transaction::Transaction, Instruction,
Keypair, Pubkey, Signer, TempClone,
fuzz_trd, show_account, solana_sdk::account::Account, solana_sdk::transaction::Transaction,
Instruction, Keypair, Pubkey, Signer, TempClone,
};
pub use anchor_client::anchor_lang::solana_program::account_info::AccountInfo;
pub use anchor_client::anchor_lang::solana_program::hash::Hash;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
pub mod fuzz_example3_fuzz_instructions {
use crate::accounts_snapshots::*;
use trdelnik_client::fuzzing::*;
#[derive(Arbitrary, Clone, DisplayIx, FuzzTestExecutor, FuzzDeserialize)]
#[derive(Arbitrary, DisplayIx, FuzzTestExecutor, FuzzDeserialize)]
pub enum FuzzInstruction {
InitVesting(InitVesting),
WithdrawUnlocked(WithdrawUnlocked),
}
#[derive(Arbitrary, Clone)]
#[derive(Arbitrary, Debug)]
pub struct InitVesting {
pub accounts: InitVestingAccounts,
pub data: InitVestingData,
}
#[derive(Arbitrary, Clone)]
#[derive(Arbitrary, Debug)]
pub struct InitVestingAccounts {
pub sender: AccountId,
pub sender_token_account: AccountId,
Expand All @@ -21,7 +21,7 @@ pub mod fuzz_example3_fuzz_instructions {
pub token_program: AccountId,
pub system_program: AccountId,
}
#[derive(Arbitrary, Clone)]
#[derive(Arbitrary, Debug)]
pub struct InitVestingData {
pub recipient: AccountId,
pub _recipient: AccountId,
Expand All @@ -30,12 +30,12 @@ pub mod fuzz_example3_fuzz_instructions {
pub end_at: u64,
pub interval: u64,
}
#[derive(Arbitrary, Clone)]
#[derive(Arbitrary, Debug)]
pub struct WithdrawUnlocked {
pub accounts: WithdrawUnlockedAccounts,
pub data: WithdrawUnlockedData,
}
#[derive(Arbitrary, Clone)]
#[derive(Arbitrary, Debug)]
pub struct WithdrawUnlockedAccounts {
pub recipient: AccountId,
pub recipient_token_account: AccountId,
Expand All @@ -46,7 +46,7 @@ pub mod fuzz_example3_fuzz_instructions {
pub token_program: AccountId,
pub system_program: AccountId,
}
#[derive(Arbitrary, Clone)]
#[derive(Arbitrary, Debug)]
pub struct WithdrawUnlockedData {}
impl<'info> IxOps<'info> for InitVesting {
type IxData = fuzz_example3::instruction::InitVesting;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,22 @@ pub enum FuzzInstruction {
impl std::fmt::Display for FuzzInstruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FuzzInstruction::InitVesting(_) => f.write_fmt(format_args!("InitVesting")),
FuzzInstruction::WithdrawUnlocked(_) => {
f.write_fmt(format_args!("WithdrawUnlocked"))
FuzzInstruction::InitVesting(ref content) => {
f.write_fmt(format_args!("InitVesting"))?;
f.write_fmt(format_args!("({0:#?})", content))
}
FuzzInstruction::WithdrawUnlocked(ref content) => {
f.write_fmt(format_args!("WithdrawUnlocked"))?;
f.write_fmt(format_args!("({0:#?})", content))
}
}
}
}
impl FuzzInstruction {
fn to_context_string(&self) -> String {
match self {
FuzzInstruction::InitVesting(_) => String::from("InitVesting"),
FuzzInstruction::WithdrawUnlocked(_) => String::from("WithdrawUnlocked"),
}
}
}
Expand Down
Loading

0 comments on commit a8d2529

Please sign in to comment.