trident_fuzz/transaction_executor.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
use std::cell::RefCell;
use solana_sdk::instruction::Instruction;
use crate::error::FuzzClientError;
use crate::error::FuzzClientErrorWithOrigin;
use crate::error::Origin;
use crate::fuzz_client::FuzzClient;
use crate::fuzz_stats::FuzzingStatistics;
use crate::ix_ops::IxOps;
use crate::snapshot::Snapshot;
use trident_config::TridentConfig;
pub struct TransactionExecutor;
impl TransactionExecutor {
pub fn process_transaction<I>(
instruction_name: &str,
client: &mut impl FuzzClient,
ix: &I,
config: &TridentConfig,
accounts: &RefCell<I::IxAccounts>,
) -> core::result::Result<(), FuzzClientErrorWithOrigin>
where
I: IxOps,
{
// Obtain the program id
let program_id = ix.get_program_id();
// Obtain the instruction data
let data = ix
.get_data(client, &mut accounts.borrow_mut())
.map_err(|e| e.with_origin(Origin::Instruction(instruction_name.to_owned())))
.expect("Data calculation expect");
// Obtain the account metas and signers
let (_signers, account_metas) = ix
.get_accounts(client, &mut accounts.borrow_mut())
.map_err(|e| e.with_origin(Origin::Instruction(instruction_name.to_owned())))
.expect("Accounts calculation expect");
// Initializes the snapshot from the account metas
let mut snapshot = Snapshot::new(&account_metas);
// Capture the accounts before the instruction is executed
snapshot.capture_before(client).unwrap();
// Create the instruction to be executed
let ixx = Instruction {
program_id,
accounts: account_metas,
data: data.clone(),
};
// If stats are enabled, log the invocation of the instruction
if config.get_fuzzing_with_stats() {
let mut stats_logger = FuzzingStatistics::new();
stats_logger.increase_invoked(instruction_name.to_owned());
// Execute the instruction
let tx_result = client.process_instructions(&[ixx]);
// Check the result of the instruction execution
match tx_result {
Ok(_) => {
// Log the successful execution of the instruction
stats_logger.increase_successful(instruction_name.to_owned());
// Capture the accounts after the instruction is executed
snapshot.capture_after(client).unwrap();
// Get the snapshot of the accounts before and after the instruction execution
let (acc_before, acc_after) = snapshot.get_snapshot();
// Let the user perform custom checks on the accounts
if let Err(e) = ix.check(acc_before, acc_after, data).map_err(|e| {
e.with_origin(Origin::Instruction(instruction_name.to_owned()))
}) {
// Log the failure of the custom check
stats_logger.increase_failed_check(instruction_name.to_owned());
stats_logger.output_serialized();
eprintln!("\x1b[31mCRASH DETECTED!\x1b[0m Custom check after the {} instruction did not pass!",instruction_name.to_owned());
panic!("{}", e)
}
stats_logger.output_serialized();
}
Err(e) => {
// Log the failure of the instruction execution
stats_logger.increase_failed(instruction_name.to_owned());
stats_logger.output_serialized();
// Let use use transaction error handler to handle the error
let raw_accounts = snapshot.get_before();
ix.tx_error_handler(e, data, raw_accounts).map_err(|e| {
FuzzClientError::from(e)
.with_origin(Origin::Instruction(instruction_name.to_owned()))
})?
}
}
} else {
// If stats are not enabled, execute the instruction directly
let tx_result = client.process_instructions(&[ixx]);
match tx_result {
Ok(_) => {
// Capture the accounts after the instruction is executed
snapshot.capture_after(client).unwrap();
// Get the snapshot of the accounts before and after the instruction execution
let (acc_before, acc_after) = snapshot.get_snapshot();
// Let the user perform custom checks on the accounts
if let Err(e) = ix.check(acc_before, acc_after, data).map_err(|e| {
e.with_origin(Origin::Instruction(instruction_name.to_owned()))
}) {
eprintln!("\x1b[31mCRASH DETECTED!\x1b[0m Custom check after the {} instruction did not pass!",instruction_name.to_owned());
panic!("{}", e)
}
}
Err(e) => {
// Let use use transaction error handler to handle the error
let raw_accounts = snapshot.get_before();
ix.tx_error_handler(e, data, raw_accounts).map_err(|e| {
FuzzClientError::from(e)
.with_origin(Origin::Instruction(instruction_name.to_owned()))
})?
}
}
}
Ok(())
}
}