use { crate::{ nonce_info::{NonceFull, NonceInfo, NoncePartial}, rent_debits::RentDebits, }, solana_program_runtime::loaded_programs::LoadedProgramsForTxBatch, solana_sdk::{ instruction::{CompiledInstruction, TRANSACTION_LEVEL_STACK_HEIGHT}, transaction::{self, TransactionError}, transaction_context::{TransactionContext, TransactionReturnData}, }, }; pub type TransactionCheckResult = (transaction::Result<()>, Option); pub struct TransactionResults { pub fee_collection_results: Vec>, pub execution_results: Vec, pub rent_debits: Vec, } /// Type safe representation of a transaction execution attempt which /// differentiates between a transaction that was executed (will be /// committed to the ledger) and a transaction which wasn't executed /// and will be dropped. /// /// Note: `Result` is not /// used because it's easy to forget that the inner `details.status` field /// is what should be checked to detect a successful transaction. This /// enum provides a convenience method `Self::was_executed_successfully` to /// make such checks hard to do incorrectly. #[derive(Debug, Clone)] pub enum TransactionExecutionResult { Executed { details: TransactionExecutionDetails, programs_modified_by_tx: Box, programs_updated_only_for_global_cache: Box, }, NotExecuted(TransactionError), } impl TransactionExecutionResult { pub fn was_executed_successfully(&self) -> bool { match self { Self::Executed { details, .. } => details.status.is_ok(), Self::NotExecuted { .. } => false, } } pub fn was_executed(&self) -> bool { match self { Self::Executed { .. } => true, Self::NotExecuted(_) => false, } } pub fn details(&self) -> Option<&TransactionExecutionDetails> { match self { Self::Executed { details, .. } => Some(details), Self::NotExecuted(_) => None, } } pub fn flattened_result(&self) -> transaction::Result<()> { match self { Self::Executed { details, .. } => details.status.clone(), Self::NotExecuted(err) => Err(err.clone()), } } } #[derive(Debug, Clone)] pub struct TransactionExecutionDetails { pub status: transaction::Result<()>, pub log_messages: Option>, pub inner_instructions: Option, pub durable_nonce_fee: Option, pub return_data: Option, pub executed_units: u64, /// The change in accounts data len for this transaction. /// NOTE: This value is valid IFF `status` is `Ok`. pub accounts_data_len_delta: i64, } #[derive(Debug, Clone)] pub enum DurableNonceFee { Valid(u64), Invalid, } impl From<&NonceFull> for DurableNonceFee { fn from(nonce: &NonceFull) -> Self { match nonce.lamports_per_signature() { Some(lamports_per_signature) => Self::Valid(lamports_per_signature), None => Self::Invalid, } } } impl DurableNonceFee { pub fn lamports_per_signature(&self) -> Option { match self { Self::Valid(lamports_per_signature) => Some(*lamports_per_signature), Self::Invalid => None, } } } /// An ordered list of compiled instructions that were invoked during a /// transaction instruction pub type InnerInstructions = Vec; #[derive(Clone, Debug, PartialEq, Eq)] pub struct InnerInstruction { pub instruction: CompiledInstruction, /// Invocation stack height of this instruction. Instruction stack height /// starts at 1 for transaction instructions. pub stack_height: u8, } /// A list of compiled instructions that were invoked during each instruction of /// a transaction pub type InnerInstructionsList = Vec; /// Extract the InnerInstructionsList from a TransactionContext pub fn inner_instructions_list_from_instruction_trace( transaction_context: &TransactionContext, ) -> InnerInstructionsList { debug_assert!(transaction_context .get_instruction_context_at_index_in_trace(0) .map(|instruction_context| instruction_context.get_stack_height() == TRANSACTION_LEVEL_STACK_HEIGHT) .unwrap_or(true)); let mut outer_instructions = Vec::new(); for index_in_trace in 0..transaction_context.get_instruction_trace_length() { if let Ok(instruction_context) = transaction_context.get_instruction_context_at_index_in_trace(index_in_trace) { let stack_height = instruction_context.get_stack_height(); if stack_height == TRANSACTION_LEVEL_STACK_HEIGHT { outer_instructions.push(Vec::new()); } else if let Some(inner_instructions) = outer_instructions.last_mut() { let stack_height = u8::try_from(stack_height).unwrap_or(u8::MAX); let instruction = CompiledInstruction::new_from_raw_parts( instruction_context .get_index_of_program_account_in_transaction( instruction_context .get_number_of_program_accounts() .saturating_sub(1), ) .unwrap_or_default() as u8, instruction_context.get_instruction_data().to_vec(), (0..instruction_context.get_number_of_instruction_accounts()) .map(|instruction_account_index| { instruction_context .get_index_of_instruction_account_in_transaction( instruction_account_index, ) .unwrap_or_default() as u8 }) .collect(), ); inner_instructions.push(InnerInstruction { instruction, stack_height, }); } else { debug_assert!(false); } } else { debug_assert!(false); } } outer_instructions } #[cfg(test)] mod tests { use {super::*, solana_sdk::transaction_context::TransactionContext}; #[test] fn test_inner_instructions_list_from_instruction_trace() { let instruction_trace = [1, 2, 1, 1, 2, 3, 2]; let mut transaction_context = TransactionContext::new(vec![], None, 3, instruction_trace.len()); for (index_in_trace, stack_height) in instruction_trace.into_iter().enumerate() { while stack_height <= transaction_context.get_instruction_context_stack_height() { transaction_context.pop().unwrap(); } if stack_height > transaction_context.get_instruction_context_stack_height() { transaction_context .get_next_instruction_context() .unwrap() .configure(&[], &[], &[index_in_trace as u8]); transaction_context.push().unwrap(); } } let inner_instructions = inner_instructions_list_from_instruction_trace(&transaction_context); assert_eq!( inner_instructions, vec![ vec![InnerInstruction { instruction: CompiledInstruction::new_from_raw_parts(0, vec![1], vec![]), stack_height: 2, }], vec![], vec![ InnerInstruction { instruction: CompiledInstruction::new_from_raw_parts(0, vec![4], vec![]), stack_height: 2, }, InnerInstruction { instruction: CompiledInstruction::new_from_raw_parts(0, vec![5], vec![]), stack_height: 3, }, InnerInstruction { instruction: CompiledInstruction::new_from_raw_parts(0, vec![6], vec![]), stack_height: 2, }, ] ] ); } }