#[cfg(feature = "conformance")]
use {
crate::conformance::{
account_state::account_to_proto, err::serialized_error_code, fd_hash::fd_hash_or_zero,
},
agave_precompiles::is_precompile,
protosol::protos::{FeeDetails as ProtoFeeDetails, TxnResult as ProtoTxnResult},
solana_instruction_error::InstructionError,
solana_message::SanitizedMessage,
};
use {
solana_account::Account,
solana_fee_structure::FeeDetails,
solana_pubkey::Pubkey,
solana_transaction_error::{TransactionError, TransactionResult},
};
pub struct TxnEffects {
pub executed: bool,
pub status: TransactionResult<()>,
pub resulting_accounts: Vec<(Pubkey, Account)>,
pub rollback_accounts: Vec<(Pubkey, Account)>,
pub return_data: Vec<u8>,
pub executed_units: u64,
pub fee_details: FeeDetails,
pub loaded_accounts_data_size: u64,
pub logs: Vec<String>,
pub cu_avail: u64,
}
impl TxnEffects {
pub fn from_unprocessed_error(err: TransactionError) -> Self {
Self {
executed: false,
status: Err(err),
resulting_accounts: vec![],
rollback_accounts: vec![],
return_data: vec![],
executed_units: 0,
fee_details: FeeDetails::new(0, 0),
loaded_accounts_data_size: 0,
logs: vec![],
cu_avail: 0,
}
}
pub fn get_account(&self, pubkey: &Pubkey) -> Option<&Account> {
self.resulting_accounts
.iter()
.find(|(pk, _)| pk == pubkey)
.map(|(_, account)| account)
}
#[cfg(feature = "conformance")]
pub fn zero_precompile_custom_error(&mut self, sanitized_message: &SanitizedMessage) {
let index = match &self.status {
Err(TransactionError::InstructionError(index, InstructionError::Custom(code)))
if *code != 0 =>
{
*index
}
_ => return,
};
let failed_on_precompile = sanitized_message
.program_instructions_iter()
.nth(usize::from(index))
.is_some_and(|(program_id, _)| is_precompile(program_id, |_| true));
if failed_on_precompile {
self.status = Err(TransactionError::InstructionError(
index,
InstructionError::Custom(0),
));
}
}
}
#[cfg(feature = "conformance")]
impl From<TxnEffects> for ProtoTxnResult {
fn from(value: TxnEffects) -> Self {
let (txn_error, instruction_error, custom_error, instruction_error_index) = value
.status
.as_ref()
.err()
.map(|transaction_error| {
let (instruction_error, custom_error, instruction_error_index) =
match transaction_error {
TransactionError::InstructionError(
instruction_error_index,
instruction_error,
) => {
let custom_error = match instruction_error {
InstructionError::Custom(custom_error) => *custom_error,
_ => 0,
};
(
serialized_error_code(instruction_error),
custom_error,
(*instruction_error_index).into(),
)
}
_ => (0, 0, 0),
};
(
serialized_error_code(transaction_error),
instruction_error,
custom_error,
instruction_error_index,
)
})
.unwrap_or((0, 0, 0, 0));
let fee_details = Some(ProtoFeeDetails {
transaction_fee: value.fee_details.transaction_fee(),
prioritization_fee: value.fee_details.prioritization_fee(),
});
let modified_accounts = value
.resulting_accounts
.into_iter()
.map(|(pubkey, account)| account_to_proto((pubkey, account)))
.collect();
let rollback_accounts = value
.rollback_accounts
.into_iter()
.map(|(pubkey, account)| account_to_proto((pubkey, account)))
.collect();
Self {
executed: value.executed,
txn_error,
instruction_error,
instruction_error_index,
custom_error,
return_data_hash: fd_hash_or_zero(&value.return_data),
executed_units: value.executed_units,
fee_details,
loaded_accounts_data_size: value.loaded_accounts_data_size,
modified_accounts,
rollback_accounts,
}
}
}