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: 1 addition & 1 deletion crates/solana-address-book/src/address_book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::collections::{HashMap, HashSet};
/// Solana addresses by their public keys, labels, and roles. It's designed
/// to help with debugging and transaction analysis by providing meaningful
/// context for addresses.
#[derive(Debug, Default)]
#[derive(Clone, Debug, Default)]
pub struct AddressBook {
addresses: HashMap<Pubkey, Vec<(String, RegisteredAddress)>>,
registered_addresses: HashSet<RegisteredAddress>,
Expand Down
36 changes: 33 additions & 3 deletions crates/testsvm-assertions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

use anyhow::*;
use litesvm::types::TransactionMetadata;
use solana_sdk::instruction::InstructionError;
use solana_sdk::{instruction::InstructionError, program_error::ProgramError};
use testsvm_core::prelude::*;

/// Provides assertion methods for failed transactions.
Expand Down Expand Up @@ -128,6 +128,30 @@ impl TXErrorAssertions {
}
}

/// Asserts that the transaction failed with a specific program error.
pub fn with_program_error<T: Into<ProgramError>>(&self, err: T) -> Result<()> {
let program_error: ProgramError = err.into();
match self.error.metadata.err.clone() {
solana_sdk::transaction::TransactionError::InstructionError(_, err) => {
let result_program_error: ProgramError = err.try_into()?;
if result_program_error == program_error {
Ok(())
} else {
Err(anyhow!(
"Expected custom program error {}, but got '{}'",
program_error,
result_program_error
))
}
}
_ => Err(anyhow!(
"Expected custom program error {}, but got instruction error '{}'",
program_error,
self.error.metadata.err.to_string()
)),
}
}

/// Returns the underlying transaction error for custom assertions.
pub fn error(&self) -> &TXError {
&self.error
Expand Down Expand Up @@ -270,7 +294,13 @@ impl TXResultAssertions for TXResult {
}

fn succeeds(self) -> Result<TXSuccessAssertions> {
let metadata = self?;
Ok(TXSuccessAssertions { metadata })
match self {
Result::Ok(metadata) => Ok(TXSuccessAssertions { metadata }),
Result::Err(e) => {
e.print_error();
e.address_book.print_all();
Err(anyhow::anyhow!("Unexpected failed transaction: {}", e))
}
}
}
}
29 changes: 18 additions & 11 deletions crates/testsvm-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,11 @@ impl TestSVM {
pub fn execute_transaction(&mut self, transaction: Transaction) -> TXResult {
match self.svm.send_transaction(transaction.clone()) {
Result::Ok(tx_result) => Result::Ok(tx_result),
Err(e) => {
let tx_error = TXError {
transaction,
metadata: e.clone(),
};
tx_error.print_error(&self.address_book);
self.address_book.print_all();
Err(Box::new(tx_error))
}
Err(e) => Err(Box::new(TXError {
transaction,
metadata: e.clone(),
address_book: self.address_book.clone(),
})),
}
}

Expand Down Expand Up @@ -160,10 +156,21 @@ impl TestSVM {
seeds: &[&[u8]],
program_id: Pubkey,
) -> Result<AccountRef<T>> {
let (pubkey, _bump) = self
let (pda, _) = self.get_pda_with_bump(label, seeds, program_id)?;
Ok(pda)
}

/// Finds a program derived address and return an [AccountRef] with proper type information and bump seed.
pub fn get_pda_with_bump<T: anchor_lang::AccountDeserialize>(
&mut self,
label: &str,
seeds: &[&[u8]],
program_id: Pubkey,
) -> Result<(AccountRef<T>, u8)> {
let (pubkey, bump) = self
.address_book
.find_pda_with_bump(label, seeds, program_id)?;
Ok(AccountRef::new(pubkey))
Ok((AccountRef::new(pubkey), bump))
}

/// Advance the time by the specified number of seconds
Expand Down
11 changes: 7 additions & 4 deletions crates/testsvm-core/src/tx_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub struct TXError {
pub transaction: Transaction,
/// Underlying failed transaction metadata
pub metadata: FailedTransactionMetadata,
/// Address book at the time of transaction failure
pub address_book: AddressBook,
}

impl Error for TXError {}
Expand All @@ -46,7 +48,7 @@ impl Display for TXError {

impl TXError {
/// Print the error details, formatted using an [AddressBook].
pub fn print_error(&self, address_book: &AddressBook) {
pub fn print_error(&self) {
println!(
"\n{} {}",
"❌".red(),
Expand All @@ -61,7 +63,8 @@ impl TXError {
);
println!(
"{}",
address_book.replace_addresses_in_text(&self.metadata.meta.pretty_logs())
self.address_book
.replace_addresses_in_text(&self.metadata.meta.pretty_logs())
);

// Log each instruction for debugging
Expand All @@ -76,7 +79,7 @@ impl TXError {
" {} {}: {}",
"Instruction".dimmed(),
i.to_string().bold(),
address_book.format_address(&program_id)
self.address_book.format_address(&program_id)
);
println!(
" {} {}",
Expand Down Expand Up @@ -111,7 +114,7 @@ impl TXError {
" {} {}: {}{}",
"Account".dimmed(),
j.to_string().bold(),
address_book.format_address(&account_key),
self.address_book.format_address(&account_key),
flags_str
);
}
Expand Down
3 changes: 2 additions & 1 deletion crates/testsvm-quarry/src/tests/test_rewarder_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ fn test_set_rewards_share_authority_check() -> Result<()> {
);

env.execute_ixs_with_signers(&[unauthorized_ix], &[&unauthorized])
.fails()?;
.fails()?
.with_anchor_error("Unauthorized")?;

// Verify share remains unchanged
let quarry_data = quarry.fetch_quarry(&env)?;
Expand Down
12 changes: 6 additions & 6 deletions devenv.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1756415044,
"lastModified": 1759939975,
"owner": "cachix",
"repo": "devenv",
"rev": "c570189b38b549141179647da3ddde249ac50fec",
"rev": "6eda3b7af3010d289e6e8e047435956fc80c1395",
"type": "github"
},
"original": {
Expand Down Expand Up @@ -40,10 +40,10 @@
]
},
"locked": {
"lastModified": 1755960406,
"lastModified": 1759523803,
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "e891a93b193fcaf2fc8012d890dc7f0befe86ec2",
"rev": "cfc9f7bb163ad8542029d303e599c0f7eee09835",
"type": "github"
},
"original": {
Expand Down Expand Up @@ -74,10 +74,10 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1755783167,
"lastModified": 1758532697,
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "4a880fb247d24fbca57269af672e8f78935b0328",
"rev": "207a4cb0e1253c7658c6736becc6eb9cace1f25f",
"type": "github"
},
"original": {
Expand Down