Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ incremented upon a breaking change and the patch version will be incremented for

**Added**

- add invariant macros for better invariant checking and error handling ([461](https://github.com/Ackee-Blockchain/trident/pull/461))
- introduce simple Fork Testing allowing forking and caching programs and accounts from desired clusters ([434](https://github.com/Ackee-Blockchain/trident/pull/434))
- add new random generation methods ([444](https://github.com/Ackee-Blockchain/trident/pull/444))
- add `AccountDiscriminator` trait to derive discriminator for account types ([443](https://github.com/Ackee-Blockchain/trident/pull/443))
Expand All @@ -24,6 +25,7 @@ incremented upon a breaking change and the patch version will be incremented for

**Changed**

- move transaction result to trident-svm crate ([461](https://github.com/Ackee-Blockchain/trident/pull/461))
- Allow initialization for Vanilla Solana projects with IDLs ([435](https://github.com/Ackee-Blockchain/trident/pull/435))
- improve invariant handling and exit-code behavior in fuzzing ([457](https://github.com/Ackee-Blockchain/trident/pull/457))

Expand Down
41 changes: 38 additions & 3 deletions Cargo.lock

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

8 changes: 6 additions & 2 deletions crates/client/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,12 +533,14 @@ mod tests {
]
}"#;

// Pin temp_dir across the await so the async state machine won't drop it early.
let _guard = &temp_dir;

// Should not create a backup for empty file
create_or_update_json_file(&root, &settings_path, new_content)
.await
.unwrap();

// Keep TempDir alive across post-await checks to avoid eager drop in async state machine.
assert!(temp_dir.path().exists());

// Verify no backup was created
Expand Down Expand Up @@ -572,12 +574,14 @@ mod tests {
]
}"#;

// Pin temp_dir across the await so the async state machine won't drop it early.
let _guard = &temp_dir;

// Should not create a backup for whitespace-only file
create_or_update_json_file(&root, &settings_path, new_content)
.await
.unwrap();

// Keep TempDir alive across post-await checks to avoid eager drop in async state machine.
assert!(temp_dir.path().exists());

// Verify no backup was created
Expand Down
3 changes: 2 additions & 1 deletion crates/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ workspace = true

# Trident-SVM
[dependencies.trident-svm]
version = "0.3.0-rc.2"
git = "https://github.com/Ackee-Blockchain/trident-svm"
branch = "develop"

[dependencies]

Expand Down
23 changes: 23 additions & 0 deletions crates/fuzz/src/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,26 @@ macro_rules! invariant {
}
};
}

/// Checks if two expressions are equal and panics with `InvariantViolation` if not.
///
/// Use this macro to check if two expressions are equal.
/// When the expressions are not equal, it will be counted and collected separately
/// from unexpected panics (bugs in fuzz test code).
///
/// # Examples
///
/// ```ignore
/// // Simple condition check
/// invariant_eq!(balance_after, balance_before - amount);
/// invariant_eq!(account.is_initialized, true, "Account is not initialized");
/// ```
#[macro_export]
macro_rules! invariant_eq {
($a:expr, $b:expr) => {
invariant!($a == $b);
};
($a:expr, $b:expr, $($msg:tt)*) => {
invariant!($a == $b, $($msg)*);
};
}
2 changes: 0 additions & 2 deletions crates/fuzz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ pub mod fuzzing {

/// Trident
pub use super::trident::flow_executor::FlowExecutor;
pub use super::trident::transaction_result::TransactionResult;
pub use super::trident::transaction_result::TransactionReturnData;
pub use super::trident::Trident;
pub use trident_fuzz_metrics::TridentFuzzingData;

Expand Down
175 changes: 48 additions & 127 deletions crates/fuzz/src/trident/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ use borsh::BorshDeserialize;
use borsh::BorshSerialize;
use solana_sdk::instruction::Instruction;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::transaction::TransactionError;
use trident_svm::prelude::TridentTransactionProcessingResult;
use trident_svm::processor::InstructionError;
use trident_svm::prelude::TridentTransactionResult;

use crate::trident::transaction_result::TransactionResult;
use crate::trident::transaction_result::TransactionReturnData;
use crate::trident::Trident;
use crate::AccountDiscriminator;

Expand Down Expand Up @@ -46,7 +42,7 @@ impl Trident {
&mut self,
instructions: &[Instruction],
log_as: Option<&str>,
) -> TransactionResult {
) -> TridentTransactionResult {
let fuzzing_metrics = std::env::var("FUZZING_METRICS");
let fuzzing_debug = std::env::var("TRIDENT_FUZZ_DEBUG");

Expand All @@ -63,8 +59,9 @@ impl Trident {
);
}
let processing_data = self.process_instructions(instructions);
self.handle_tx_result(&processing_data, log_as, instructions);

self.handle_tx_result(&processing_data, log_as, instructions)
processing_data
}

/// Deploys an entrypoint program to the SVM runtime
Expand Down Expand Up @@ -284,10 +281,7 @@ impl Trident {
panic!("Not yet implemented for TridentSVM");
}

fn process_instructions(
&mut self,
instructions: &[Instruction],
) -> TridentTransactionProcessingResult {
fn process_instructions(&mut self, instructions: &[Instruction]) -> TridentTransactionResult {
// there should be at least 1 RW fee-payer account.
// But we do not pay for TX currently so has to be manually updated
// tx.message.header.num_required_signatures = 1;
Expand Down Expand Up @@ -393,131 +387,58 @@ impl Trident {

fn handle_tx_result(
&mut self,
tx_processing_result: &TridentTransactionProcessingResult,
tx_processing_result: &TridentTransactionResult,
log_as: Option<&str>,
instructions: &[Instruction],
) -> TransactionResult {
) {
let fuzzing_metrics = std::env::var("FUZZING_METRICS");
let fuzzing_debug = std::env::var("TRIDENT_FUZZ_DEBUG");

// NOTE: for now we just expect that one transaction was executed
let tx_result = &tx_processing_result.get_result().processing_results[0];
let is_program_failed_to_complete = tx_processing_result.is_program_failed_to_complete();

let log_messages = tx_processing_result.logs();

if is_program_failed_to_complete && fuzzing_metrics.is_ok() {
if fuzzing_debug.is_ok() {
trident_svm::prelude::trident_svm_log::log_message(
"TRANSACTION PANICKED",
trident_svm::prelude::Level::Error,
);
}
if let Some(log_as) = log_as {
let rng = self.rng.get_seed();
// TODO format instructions
let tx = format!("{:#?}", instructions);
self.fuzzing_data.add_transaction_panicked(
log_as,
rng,
"Program failed to complete".to_string(),
Some(log_messages.clone()),
tx,
);
}
}

let transaction_timestamp = tx_processing_result.get_transaction_timestamp();
let tx_result = &tx_processing_result.status();

match tx_result {
Ok(result) => match result {
trident_svm::prelude::solana_svm::transaction_processing_result::ProcessedTransaction::Executed(executed_transaction) => match &executed_transaction.execution_details.status {
Ok(_) => {
let transaction_return_data = executed_transaction
.execution_details
.return_data
.clone()
.map(|return_data| TransactionReturnData {
program_id: return_data.program_id,
data: return_data.data,
});
// Record successful execution
if fuzzing_metrics.is_ok() && log_as.is_some() {
if let Some(log_as) = log_as {
self.fuzzing_data
.add_successful_transaction(log_as);
}
}
TransactionResult::new(
Ok(()),
executed_transaction
.execution_details
.log_messages
.clone()
.unwrap_or_default(),
transaction_timestamp,
transaction_return_data,
)
},
Err(transaction_error) => {
let transaction_return_data = executed_transaction
.execution_details
.return_data
.clone()
.map(|return_data| TransactionReturnData {
program_id: return_data.program_id,
data: return_data.data,
});
if let TransactionError::InstructionError(_error_code, instruction_error) =
&transaction_error
{
match instruction_error {
InstructionError::ProgramFailedToComplete => {
if fuzzing_metrics.is_ok() {
if fuzzing_debug.is_ok() {
trident_svm::prelude::trident_svm_log::log_message(
"TRANSACTION PANICKED",
trident_svm::prelude::Level::Error,
);
}
if let Some(log_as) = log_as {
let rng = self.rng.get_seed();
// TODO format instructions
let tx = format!("{:#?}", instructions);
self.fuzzing_data.add_transaction_panicked(
log_as,
rng,
instruction_error.to_string(),
executed_transaction.execution_details.log_messages.clone(),
tx,
);
}
}
}
InstructionError::Custom(error_code) => {
if fuzzing_metrics.is_ok() && log_as.is_some() {
if let Some(log_as) = log_as {
self.fuzzing_data.add_custom_instruction_error(
log_as,
error_code,
executed_transaction.execution_details.log_messages.clone(),
);
}
}
}
_ => {
if fuzzing_metrics.is_ok() && log_as.is_some() {
if let Some(log_as) = log_as {
self.fuzzing_data.add_failed_transaction(
log_as,
instruction_error.to_string(),
executed_transaction.execution_details.log_messages.clone(),
);
}
}
}
}
} else if fuzzing_metrics.is_ok() && log_as.is_some() {
if let Some(log_as) = log_as {
self.fuzzing_data.add_failed_transaction(
log_as,
transaction_error.to_string(),
executed_transaction.execution_details.log_messages.clone(),
);
}
}
TransactionResult::new(
Err(transaction_error.clone()),
executed_transaction
.execution_details
.log_messages
.clone()
.unwrap_or_default(),
transaction_timestamp,
transaction_return_data,
)
},
},
trident_svm::prelude::solana_svm::transaction_processing_result::ProcessedTransaction::FeesOnly(_) => todo!(),
},
Ok(_) => {
if fuzzing_metrics.is_ok() && log_as.is_some() {
if let Some(log_as) = log_as {
self.fuzzing_data.add_successful_transaction(log_as);
}
}
}
Err(transaction_error) => {
TransactionResult::new(Err(transaction_error.clone()), vec![], transaction_timestamp, None)
if fuzzing_metrics.is_ok() && log_as.is_some() {
if let Some(log_as) = log_as {
self.fuzzing_data.add_failed_transaction(
log_as,
transaction_error.to_string(),
Some(log_messages),
);
}
}
}
}
}
Expand Down
1 change: 0 additions & 1 deletion crates/fuzz/src/trident/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use crate::trident_rng::TridentRng;
mod client;
pub mod flow_executor;
mod system;
pub mod transaction_result;

mod metrics;
mod progress;
Expand Down
Loading
Loading