diff --git a/programs/bpf_loader/src/syscalls/mod.rs b/programs/bpf_loader/src/syscalls/mod.rs index f1aeba3b80f79c..af76b18dc0264f 100644 --- a/programs/bpf_loader/src/syscalls/mod.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -1710,32 +1710,20 @@ declare_syscall!( result ); + // Reverse iterate through the instruction trace, + // ignoring anything except instructions on the same level let stack_height = invoke_context.get_stack_height(); let instruction_trace = invoke_context.transaction_context.get_instruction_trace(); - let instruction_context = if stack_height == TRANSACTION_LEVEL_STACK_HEIGHT { - // pick one of the top-level instructions - instruction_trace - .len() - .checked_sub(2) - .and_then(|result| result.checked_sub(index as usize)) - .and_then(|index| instruction_trace.get(index)) - .and_then(|instruction_list| instruction_list.first()) - } else { - // Walk the last list of inner instructions - instruction_trace.last().and_then(|inners| { - let mut current_index = 0; - inners.iter().rev().skip(1).find(|instruction_context| { - if stack_height == instruction_context.get_stack_height() { - if index == current_index { - return true; - } else { - current_index = current_index.saturating_add(1); - } - } - false - }) - }) - }; + let mut current_index = 0; + let instruction_context = instruction_trace.iter().rev().find(|instruction_context| { + if instruction_context.get_stack_height() == stack_height { + if index.saturating_add(1) == current_index { + return true; + } + current_index = current_index.saturating_add(1); + } + false + }); if let Some(instruction_context) = instruction_context { let ProcessedSiblingInstruction { diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 2af2099d2b4a92..739192a9bb4687 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -121,7 +121,7 @@ use { hash::{extend_and_hash, hashv, Hash}, incinerator, inflation::Inflation, - instruction::CompiledInstruction, + instruction::{CompiledInstruction, TRANSACTION_LEVEL_STACK_HEIGHT}, lamports::LamportsError, message::{AccountKeys, SanitizedMessage}, native_loader, @@ -144,7 +144,7 @@ use { TransactionVerificationMode, VersionedTransaction, }, transaction_context::{ - ExecutionRecord, InstructionTrace, TransactionAccount, TransactionContext, + ExecutionRecord, InstructionContext, TransactionAccount, TransactionContext, TransactionReturnData, }, }, @@ -744,40 +744,45 @@ pub type InnerInstructions = Vec; /// a transaction pub type InnerInstructionsList = Vec; -/// Convert from an InstructionTrace to InnerInstructionsList +/// Convert from an &[InstructionContext] to InnerInstructionsList pub fn inner_instructions_list_from_instruction_trace( - instruction_trace: &InstructionTrace, + instruction_trace: &[InstructionContext], ) -> InnerInstructionsList { - instruction_trace - .iter() - .map(|inner_instructions_trace| { - inner_instructions_trace - .iter() - .skip(1) - .map(|instruction_context| { - 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(), - ) - }) - .collect() - }) - .collect() + debug_assert!(instruction_trace + .first() + .map(|instruction_context| instruction_context.get_stack_height() + == TRANSACTION_LEVEL_STACK_HEIGHT) + .unwrap_or(true)); + let mut outer_instructions = Vec::new(); + for instruction_context in instruction_trace.iter() { + if instruction_context.get_stack_height() == TRANSACTION_LEVEL_STACK_HEIGHT { + outer_instructions.push(Vec::new()); + } else { + outer_instructions + .last_mut() + .unwrap() + .push(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(), + )); + } + } + outer_instructions } /// A list of log messages emitted during a transaction @@ -18892,17 +18897,13 @@ pub(crate) mod tests { #[test] fn test_inner_instructions_list_from_instruction_trace() { let instruction_trace = vec![ - vec![ - InstructionContext::new(0, 0, &[], &[], &[1]), - InstructionContext::new(1, 0, &[], &[], &[2]), - ], - vec![], - vec![ - InstructionContext::new(0, 0, &[], &[], &[3]), - InstructionContext::new(1, 0, &[], &[], &[4]), - InstructionContext::new(2, 0, &[], &[], &[5]), - InstructionContext::new(1, 0, &[], &[], &[6]), - ], + InstructionContext::new(0, 0, &[], &[], &[0]), + InstructionContext::new(1, 0, &[], &[], &[1]), + InstructionContext::new(0, 0, &[], &[], &[2]), + InstructionContext::new(0, 0, &[], &[], &[3]), + InstructionContext::new(1, 0, &[], &[], &[4]), + InstructionContext::new(2, 0, &[], &[], &[5]), + InstructionContext::new(1, 0, &[], &[], &[6]), ]; let inner_instructions = inner_instructions_list_from_instruction_trace(&instruction_trace); @@ -18910,7 +18911,7 @@ pub(crate) mod tests { assert_eq!( inner_instructions, vec![ - vec![CompiledInstruction::new_from_raw_parts(0, vec![2], vec![])], + vec![CompiledInstruction::new_from_raw_parts(0, vec![1], vec![])], vec![], vec![ CompiledInstruction::new_from_raw_parts(0, vec![4], vec![]), diff --git a/sdk/src/transaction_context.rs b/sdk/src/transaction_context.rs index eabd59c176b36c..ffb19e005cf11a 100644 --- a/sdk/src/transaction_context.rs +++ b/sdk/src/transaction_context.rs @@ -48,8 +48,8 @@ pub struct TransactionContext { account_touched_flags: RefCell>>, instruction_context_capacity: usize, instruction_stack: Vec, - number_of_instructions_at_transaction_level: usize, - instruction_trace: InstructionTrace, + _number_of_instructions_at_transaction_level: usize, + instruction_trace: Vec, return_data: TransactionReturnData, accounts_resize_delta: RefCell, rent: Option, @@ -75,8 +75,9 @@ impl TransactionContext { account_touched_flags: RefCell::new(Pin::new(account_touched_flags.into_boxed_slice())), instruction_context_capacity, instruction_stack: Vec::with_capacity(instruction_context_capacity), - number_of_instructions_at_transaction_level, - instruction_trace: Vec::with_capacity(number_of_instructions_at_transaction_level), + _number_of_instructions_at_transaction_level: + number_of_instructions_at_transaction_level, + instruction_trace: Vec::new(), return_data: TransactionReturnData::default(), accounts_resize_delta: RefCell::new(0), rent, @@ -144,22 +145,13 @@ impl TransactionContext { &self, level: usize, ) -> Result<&InstructionContext, InstructionError> { - let top_level_index = *self + let index_in_trace = *self .instruction_stack - .first() + .get(level) .ok_or(InstructionError::CallDepth)?; - let cpi_index = if level == 0 { - 0 - } else { - *self - .instruction_stack - .get(level) - .ok_or(InstructionError::CallDepth)? - }; let instruction_context = self .instruction_trace - .get(top_level_index) - .and_then(|instruction_trace| instruction_trace.get(cpi_index)) + .get(index_in_trace) .ok_or(InstructionError::CallDepth)?; debug_assert_eq!(instruction_context.nesting_level, level); Ok(instruction_context) @@ -192,50 +184,33 @@ impl TransactionContext { instruction_accounts: &[InstructionAccount], instruction_data: &[u8], ) -> Result<(), InstructionError> { + if !self.instruction_stack.is_empty() + && self.is_early_verification_of_account_modifications_enabled() + { + let caller_instruction_context = self.get_current_instruction_context()?; + let original_caller_instruction_accounts_lamport_sum = + caller_instruction_context.instruction_accounts_lamport_sum; + let current_caller_instruction_accounts_lamport_sum = self + .instruction_accounts_lamport_sum( + &caller_instruction_context.instruction_accounts, + )?; + if original_caller_instruction_accounts_lamport_sum + != current_caller_instruction_accounts_lamport_sum + { + return Err(InstructionError::UnbalancedInstruction); + } + } let callee_instruction_accounts_lamport_sum = self.instruction_accounts_lamport_sum(instruction_accounts)?; - let index_in_trace = if self.instruction_stack.is_empty() { - debug_assert!( - self.instruction_trace.len() < self.number_of_instructions_at_transaction_level - ); - let instruction_context = InstructionContext { - nesting_level: self.instruction_stack.len(), - instruction_accounts_lamport_sum: callee_instruction_accounts_lamport_sum, - program_accounts: program_accounts.to_vec(), - instruction_accounts: instruction_accounts.to_vec(), - instruction_data: instruction_data.to_vec(), - }; - self.instruction_trace.push(vec![instruction_context]); - self.instruction_trace.len().saturating_sub(1) - } else { - if self.is_early_verification_of_account_modifications_enabled() { - let caller_instruction_context = self.get_current_instruction_context()?; - let original_caller_instruction_accounts_lamport_sum = - caller_instruction_context.instruction_accounts_lamport_sum; - let current_caller_instruction_accounts_lamport_sum = self - .instruction_accounts_lamport_sum( - &caller_instruction_context.instruction_accounts, - )?; - if original_caller_instruction_accounts_lamport_sum - != current_caller_instruction_accounts_lamport_sum - { - return Err(InstructionError::UnbalancedInstruction); - } - } - if let Some(instruction_trace) = self.instruction_trace.last_mut() { - let instruction_context = InstructionContext { - nesting_level: self.instruction_stack.len(), - instruction_accounts_lamport_sum: callee_instruction_accounts_lamport_sum, - program_accounts: program_accounts.to_vec(), - instruction_accounts: instruction_accounts.to_vec(), - instruction_data: instruction_data.to_vec(), - }; - instruction_trace.push(instruction_context); - instruction_trace.len().saturating_sub(1) - } else { - return Err(InstructionError::CallDepth); - } + let instruction_context = InstructionContext { + nesting_level: self.instruction_stack.len(), + instruction_accounts_lamport_sum: callee_instruction_accounts_lamport_sum, + program_accounts: program_accounts.to_vec(), + instruction_accounts: instruction_accounts.to_vec(), + instruction_data: instruction_data.to_vec(), }; + let index_in_trace = self.instruction_trace.len(); + self.instruction_trace.push(instruction_context); if self.instruction_stack.len() >= self.instruction_context_capacity { return Err(InstructionError::CallDepth); } @@ -294,7 +269,7 @@ impl TransactionContext { } /// Returns instruction trace - pub fn get_instruction_trace(&self) -> &InstructionTrace { + pub fn get_instruction_trace(&self) -> &[InstructionContext] { &self.instruction_trace } @@ -340,9 +315,6 @@ pub struct TransactionReturnData { pub data: Vec, } -/// List of (stack height, instruction) for each top-level instruction -pub type InstructionTrace = Vec>; - /// Loaded instruction shared between runtime and programs. /// /// This context is valid for the entire duration of a (possibly cross program) instruction being processed. @@ -912,7 +884,7 @@ impl<'a> BorrowedAccount<'a> { /// Everything that needs to be recorded from a TransactionContext after execution pub struct ExecutionRecord { pub accounts: Vec, - pub instruction_trace: InstructionTrace, + pub instruction_trace: Vec, pub return_data: TransactionReturnData, pub changed_account_count: u64, pub total_size_of_all_accounts: u64,