diff --git a/cli/src/program.rs b/cli/src/program.rs index 1760185592..65bfcef83e 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -1991,8 +1991,8 @@ fn read_and_verify_elf(program_location: &str) -> Result, Box::from_elf( diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs index 75e10294eb..71b15cb855 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -6,7 +6,6 @@ use { }, solana_sdk::{ account::{AccountSharedData, ReadableAccount}, - account_utils::StateMut, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, compute_budget::ComputeBudget, feature_set::{ @@ -15,7 +14,7 @@ use { }, hash::Hash, instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, - keyed_account::{create_keyed_accounts_unified, keyed_account_at_index, KeyedAccount}, + keyed_account::{create_keyed_accounts_unified, KeyedAccount}, native_loader, pubkey::Pubkey, rent::Rent, @@ -135,7 +134,7 @@ impl<'a> StackFrame<'a> { } pub struct InvokeContext<'a> { - pub transaction_context: &'a TransactionContext, + pub transaction_context: &'a mut TransactionContext, pub return_data: (Pubkey, Vec), invoke_stack: Vec>, rent: Rent, @@ -158,7 +157,7 @@ pub struct InvokeContext<'a> { impl<'a> InvokeContext<'a> { #[allow(clippy::too_many_arguments)] pub fn new( - transaction_context: &'a TransactionContext, + transaction_context: &'a mut TransactionContext, rent: Rent, builtin_programs: &'a [BuiltinProgram], sysvars: &'a [(Pubkey, Vec)], @@ -194,7 +193,7 @@ impl<'a> InvokeContext<'a> { } pub fn new_mock( - transaction_context: &'a TransactionContext, + transaction_context: &'a mut TransactionContext, builtin_programs: &'a [BuiltinProgram], ) -> Self { Self::new( @@ -218,8 +217,13 @@ impl<'a> InvokeContext<'a> { &mut self, instruction_accounts: &[InstructionAccount], program_indices: &[usize], + instruction_data: &[u8], ) -> Result<(), InstructionError> { - if self.invoke_stack.len() > self.compute_budget.max_invoke_depth { + if self + .transaction_context + .get_instruction_context_stack_height() + > self.compute_budget.max_invoke_depth + { return Err(InstructionError::CallDepth); } @@ -234,7 +238,11 @@ impl<'a> InvokeContext<'a> { { return Err(InstructionError::UnsupportedProgramId); } - if self.invoke_stack.is_empty() { + if self + .transaction_context + .get_instruction_context_stack_height() + == 0 + { let mut compute_budget = self.compute_budget; if !self.feature_set.is_active(&tx_wide_compute_cap::id()) && self.feature_set.is_active(&neon_evm_compute_budget::id()) @@ -278,15 +286,26 @@ impl<'a> InvokeContext<'a> { }; visit_each_account_once(instruction_accounts, &mut work)?; } else { - let contains = self - .invoke_stack - .iter() - .any(|frame| frame.program_id() == program_id); - let is_last = if let Some(last_frame) = self.invoke_stack.last() { - last_frame.program_id() == program_id - } else { - false - }; + let contains = (0..self + .transaction_context + .get_instruction_context_stack_height()) + .any(|level| { + self.transaction_context + .get_instruction_context_at(level) + .and_then(|instruction_context| { + instruction_context.try_borrow_program_account(self.transaction_context) + }) + .map(|program_account| Some(program_account.get_key()) == program_id) + .unwrap_or_else(|_| program_id.is_none()) + }); + let is_last = self + .transaction_context + .get_current_instruction_context() + .and_then(|instruction_context| { + instruction_context.try_borrow_program_account(self.transaction_context) + }) + .map(|program_account| Some(program_account.get_key()) == program_id) + .unwrap_or_else(|_| program_id.is_none()); if contains && !is_last { // Reentrancy not allowed unless caller is calling itself return Err(InstructionError::ReentrancyNotAllowed); @@ -318,21 +337,27 @@ impl<'a> InvokeContext<'a> { })) .collect::>(); + // Unsafe will be removed together with the keyed_accounts self.invoke_stack.push(StackFrame::new( program_indices.len(), - create_keyed_accounts_unified(keyed_accounts.as_slice()), + create_keyed_accounts_unified(unsafe { + std::mem::transmute(keyed_accounts.as_slice()) + }), )); - Ok(()) + self.transaction_context + .push(program_indices, instruction_accounts, instruction_data) } /// Pop a stack frame from the invocation stack - pub fn pop(&mut self) { + pub fn pop(&mut self) -> Result<(), InstructionError> { self.invoke_stack.pop(); + self.transaction_context.pop() } /// Current depth of the invocation stack pub fn invoke_depth(&self) -> usize { - self.invoke_stack.len() + self.transaction_context + .get_instruction_context_stack_height() } /// Verify the results of an instruction @@ -341,12 +366,11 @@ impl<'a> InvokeContext<'a> { instruction_accounts: &[InstructionAccount], program_indices: &[usize], ) -> Result<(), InstructionError> { - let program_id = self - .invoke_stack - .last() - .and_then(|frame| frame.program_id()) - .ok_or(InstructionError::CallDepth)?; let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id()); + let program_id = self + .transaction_context + .get_program_key() + .map_err(|_| InstructionError::CallDepth)?; // Verify all executable accounts have zero outstanding refs for account_index in program_indices.iter() { @@ -414,23 +438,18 @@ impl<'a> InvokeContext<'a> { fn verify_and_update( &mut self, instruction_accounts: &[InstructionAccount], - caller_write_privileges: Option<&[bool]>, + before_instruction_context_push: bool, ) -> Result<(), InstructionError> { let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id()); - let program_id = self - .invoke_stack - .last() - .and_then(|frame| frame.program_id()) - .ok_or(InstructionError::CallDepth)?; - let rent = &self.rent; - let log_collector = &self.log_collector; - let transaction_context = self.transaction_context; - let pre_accounts = &mut self.pre_accounts; - let timings = &mut self.timings; + let transaction_context = &self.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; + let program_id = transaction_context + .get_program_key() + .map_err(|_| InstructionError::CallDepth)?; // Verify the per-account instruction results let (mut pre_sum, mut post_sum) = (0_u128, 0_u128); - let mut work = |index_in_instruction: usize, instruction_account: &InstructionAccount| { + let mut work = |_index_in_instruction: usize, instruction_account: &InstructionAccount| { if instruction_account.index_in_transaction < transaction_context.get_number_of_accounts() { @@ -438,13 +457,18 @@ impl<'a> InvokeContext<'a> { .get_key_of_account_at_index(instruction_account.index_in_transaction); let account = transaction_context .get_account_at_index(instruction_account.index_in_transaction); - let is_writable = if let Some(caller_write_privileges) = caller_write_privileges { - caller_write_privileges[index_in_instruction] + let is_writable = if before_instruction_context_push { + instruction_context + .try_borrow_account( + self.transaction_context, + instruction_account.index_in_caller, + )? + .is_writable() } else { instruction_account.is_writable }; // Find the matching PreAccount - for pre_account in pre_accounts.iter_mut() { + for pre_account in self.pre_accounts.iter_mut() { if key == pre_account.key() { { // Verify account has no outstanding references @@ -457,15 +481,15 @@ impl<'a> InvokeContext<'a> { .verify( program_id, is_writable, - rent, + &self.rent, &account, - timings, + &mut self.timings, false, do_support_realloc, ) .map_err(|err| { ic_logger_msg!( - log_collector, + self.log_collector, "failed to verify account {}: {}", key, err @@ -502,7 +526,7 @@ impl<'a> InvokeContext<'a> { instruction: Instruction, signers: &[Pubkey], ) -> Result<(), InstructionError> { - let (instruction_accounts, caller_write_privileges, program_indices) = + let (instruction_accounts, program_indices) = self.prepare_instruction(&instruction, signers)?; let mut prev_account_sizes = Vec::with_capacity(instruction_accounts.len()); for instruction_account in instruction_accounts.iter() { @@ -519,7 +543,6 @@ impl<'a> InvokeContext<'a> { self.process_instruction( &instruction.data, &instruction_accounts, - Some(&caller_write_privileges), &program_indices, &mut compute_units_consumed, )?; @@ -556,11 +579,11 @@ impl<'a> InvokeContext<'a> { &mut self, instruction: &Instruction, signers: &[Pubkey], - ) -> Result<(Vec, Vec, Vec), InstructionError> { + ) -> Result<(Vec, Vec), InstructionError> { // Finds the index of each account in the instruction by its pubkey. // Then normalizes / unifies the privileges of duplicate accounts. // Note: This works like visit_each_account_once() and is an O(n^2) algorithm too. - let caller_keyed_accounts = self.get_keyed_accounts()?; + let instruction_context = self.transaction_context.get_current_instruction_context()?; let mut deduplicated_instruction_accounts: Vec = Vec::new(); let mut duplicate_indicies = Vec::with_capacity(instruction.accounts.len()); for account_meta in instruction.accounts.iter() { @@ -587,9 +610,8 @@ impl<'a> InvokeContext<'a> { instruction_account.is_signer |= account_meta.is_signer; instruction_account.is_writable |= account_meta.is_writable; } else { - let index_in_caller = caller_keyed_accounts - .iter() - .position(|keyed_account| *keyed_account.unsigned_key() == account_meta.pubkey) + let index_in_caller = instruction_context + .find_index_of_account(self.transaction_context, &account_meta.pubkey) .ok_or_else(|| { ic_msg!( self, @@ -598,6 +620,33 @@ impl<'a> InvokeContext<'a> { ); InstructionError::MissingAccount })?; + let borrowed_account = instruction_context + .try_borrow_account(self.transaction_context, index_in_caller)?; + + // Readonly in caller cannot become writable in callee + if account_meta.is_writable && !borrowed_account.is_writable() { + ic_msg!( + self, + "{}'s writable privilege escalated", + borrowed_account.get_key(), + ); + return Err(InstructionError::PrivilegeEscalation); + } + + // To be signed in the callee, + // it must be either signed in the caller or by the program + if account_meta.is_signer + && !(borrowed_account.is_signer() + || signers.contains(borrowed_account.get_key())) + { + ic_msg!( + self, + "{}'s signer privilege escalated", + borrowed_account.get_key() + ); + return Err(InstructionError::PrivilegeEscalation); + } + duplicate_indicies.push(deduplicated_instruction_accounts.len()); deduplicated_instruction_accounts.push(InstructionAccount { index_in_transaction, @@ -612,66 +661,25 @@ impl<'a> InvokeContext<'a> { .map(|duplicate_index| deduplicated_instruction_accounts[duplicate_index].clone()) .collect(); - // Check for privilege escalation - let caller_write_privileges = instruction_accounts - .iter() - .map(|instruction_account| { - let keyed_account = &caller_keyed_accounts[instruction_account.index_in_caller]; - - // Readonly in caller cannot become writable in callee - if instruction_account.is_writable && !keyed_account.is_writable() { - ic_msg!( - self, - "{}'s writable privilege escalated", - keyed_account.unsigned_key(), - ); - return Err(InstructionError::PrivilegeEscalation); - } - - // To be signed in the callee, - // it must be either signed in the caller or by the program - if instruction_account.is_signer - && !(keyed_account.signer_key().is_some() - || signers.contains(keyed_account.unsigned_key())) - { - ic_msg!( - self, - "{}'s signer privilege escalated", - keyed_account.unsigned_key() - ); - return Err(InstructionError::PrivilegeEscalation); - } - - Ok(keyed_account.is_writable()) - }) - .collect::, InstructionError>>()?; - // Find and validate executables / program accounts let callee_program_id = instruction.program_id; - let program_account_index = caller_keyed_accounts - .iter() - .find(|keyed_account| &callee_program_id == keyed_account.unsigned_key()) - .and_then(|_keyed_account| { - self.transaction_context - .find_index_of_program_account(&callee_program_id) - }) + let program_account_index = instruction_context + .find_index_of_account(self.transaction_context, &callee_program_id) .ok_or_else(|| { ic_msg!(self, "Unknown program {}", callee_program_id); InstructionError::MissingAccount })?; - let program_account = self - .transaction_context - .get_account_at_index(program_account_index) - .borrow(); - if !program_account.executable() { + let borrowed_program_account = instruction_context + .try_borrow_account(self.transaction_context, program_account_index)?; + if !borrowed_program_account.is_executable() { ic_msg!(self, "Account {} is not executable", callee_program_id); return Err(InstructionError::AccountNotExecutable); } let mut program_indices = vec![]; - if program_account.owner() == &bpf_loader_upgradeable::id() { + if borrowed_program_account.get_owner() == &bpf_loader_upgradeable::id() { if let UpgradeableLoaderState::Program { programdata_address, - } = program_account.state()? + } = borrowed_program_account.get_state()? { if let Some(programdata_account_index) = self .transaction_context @@ -695,21 +703,16 @@ impl<'a> InvokeContext<'a> { return Err(InstructionError::MissingAccount); } } - program_indices.push(program_account_index); + program_indices.push(borrowed_program_account.get_index_in_transaction()); - Ok(( - instruction_accounts, - caller_write_privileges, - program_indices, - )) + Ok((instruction_accounts, program_indices)) } - /// Processes a cross-program instruction and returns how many compute units were used + /// Processes an instruction and returns how many compute units were used pub fn process_instruction( &mut self, instruction_data: &[u8], instruction_accounts: &[InstructionAccount], - caller_write_privileges: Option<&[bool]>, program_indices: &[usize], compute_units_consumed: &mut u64, ) -> Result<(), InstructionError> { @@ -719,14 +722,17 @@ impl<'a> InvokeContext<'a> { .map(|index| *self.transaction_context.get_key_of_account_at_index(*index)) .unwrap_or_else(native_loader::id); - let is_lowest_invocation_level = self.invoke_stack.is_empty(); + let is_lowest_invocation_level = self + .transaction_context + .get_instruction_context_stack_height() + == 0; if is_lowest_invocation_level { if let Some(instruction_recorder) = &self.instruction_recorder { instruction_recorder.borrow_mut().begin_next_recording(); } } else { // Verify the calling program hasn't misbehaved - self.verify_and_update(instruction_accounts, caller_write_privileges)?; + self.verify_and_update(instruction_accounts, true)?; // Record instruction if let Some(instruction_recorder) = &self.instruction_recorder { @@ -748,7 +754,7 @@ impl<'a> InvokeContext<'a> { } let result = self - .push(instruction_accounts, program_indices) + .push(instruction_accounts, program_indices, instruction_data) .and_then(|_| { self.return_data = (program_id, Vec::new()); let pre_remaining_units = self.compute_meter.borrow().get_remaining(); @@ -761,12 +767,12 @@ impl<'a> InvokeContext<'a> { if is_lowest_invocation_level { self.verify(instruction_accounts, program_indices) } else { - self.verify_and_update(instruction_accounts, None) + self.verify_and_update(instruction_accounts, false) } }); // Pop the invoke_stack to restore previous state - self.pop(); + let _ = self.pop(); result } @@ -775,14 +781,16 @@ impl<'a> InvokeContext<'a> { &mut self, instruction_data: &[u8], ) -> Result<(), InstructionError> { - let keyed_accounts = self.get_keyed_accounts()?; - let root_account = keyed_account_at_index(keyed_accounts, 0) + let instruction_context = self.transaction_context.get_current_instruction_context()?; + let borrowed_root_account = instruction_context + .try_borrow_account(self.transaction_context, 0) .map_err(|_| InstructionError::UnsupportedProgramId)?; - let root_id = root_account.unsigned_key(); - let owner_id = &root_account.owner()?; + let root_id = borrowed_root_account.get_key(); + let owner_id = borrowed_root_account.get_owner(); if solana_sdk::native_loader::check_id(owner_id) { for entry in self.builtin_programs { if entry.program_id == *root_id { + drop(borrowed_root_account); // Call the builtin program return (entry.process_instruction)( 1, // root_id to be skipped @@ -792,6 +800,7 @@ impl<'a> InvokeContext<'a> { } } if !self.feature_set.is_active(&remove_native_loader::id()) { + drop(borrowed_root_account); let native_loader = NativeLoader::default(); // Call the program via the native loader return native_loader.process_instruction(0, instruction_data, self); @@ -799,6 +808,7 @@ impl<'a> InvokeContext<'a> { } else { for entry in self.builtin_programs { if entry.program_id == *owner_id { + drop(borrowed_root_account); // Call the program via a builtin loader return (entry.process_instruction)( 0, // no root_id was provided @@ -811,27 +821,6 @@ impl<'a> InvokeContext<'a> { Err(InstructionError::UnsupportedProgramId) } - /// Get the program ID of the currently executing program - pub fn get_caller(&self) -> Result<&Pubkey, InstructionError> { - self.invoke_stack - .last() - .and_then(|frame| frame.program_id()) - .ok_or(InstructionError::CallDepth) - } - - /// Get the owner of the currently executing program - pub fn get_loader(&self) -> Result { - let frame = self - .invoke_stack - .last() - .ok_or(InstructionError::CallDepth)?; - let first_instruction_account = frame - .number_of_program_accounts - .checked_sub(1) - .ok_or(InstructionError::CallDepth)?; - frame.keyed_accounts[first_instruction_account].owner() - } - /// Removes the first keyed account #[deprecated( since = "1.9.0", @@ -857,17 +846,6 @@ impl<'a> InvokeContext<'a> { .ok_or(InstructionError::CallDepth) } - /// Get the list of keyed accounts without the chain of program accounts - /// - /// Note: This only contains the `KeyedAccount`s passed by the caller. - pub fn get_instruction_keyed_accounts(&self) -> Result<&[KeyedAccount], InstructionError> { - let frame = self - .invoke_stack - .last() - .ok_or(InstructionError::CallDepth)?; - Ok(&frame.keyed_accounts[frame.number_of_program_accounts..]) - } - /// Get this invocation's LogCollector pub fn get_log_collector(&self) -> Option>> { self.log_collector.clone() @@ -975,13 +953,13 @@ pub fn with_mock_invoke_context R>( }]; let preparation = prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( preparation.transaction_accounts, - ComputeBudget::default().max_invoke_depth, + ComputeBudget::default().max_invoke_depth.saturating_add(1), ); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); invoke_context - .push(&preparation.instruction_accounts, &program_indices) + .push(&preparation.instruction_accounts, &program_indices, &[]) .unwrap(); callback(&mut invoke_context) } @@ -1003,15 +981,20 @@ pub fn mock_process_instruction_with_sysvars( preparation .transaction_accounts .push((*loader_id, processor_account)); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( preparation.transaction_accounts, - ComputeBudget::default().max_invoke_depth, + ComputeBudget::default().max_invoke_depth.saturating_add(1), ); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); invoke_context.sysvars = sysvars; let result = invoke_context - .push(&preparation.instruction_accounts, &program_indices) + .push( + &preparation.instruction_accounts, + &program_indices, + instruction_data, + ) .and_then(|_| process_instruction(1, instruction_data, &mut invoke_context)); + invoke_context.pop().unwrap(); assert_eq!(result, expected_result); let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap(); transaction_accounts.pop(); @@ -1062,7 +1045,10 @@ mod tests { use { super::*, serde::{Deserialize, Serialize}, - solana_sdk::account::{ReadableAccount, WritableAccount}, + solana_sdk::{ + account::{ReadableAccount, WritableAccount}, + keyed_account::keyed_account_at_index, + }, }; #[derive(Debug, Serialize, Deserialize)] @@ -1163,7 +1149,8 @@ mod tests { data: &[u8], invoke_context: &mut InvokeContext, ) -> Result<(), InstructionError> { - let program_id = invoke_context.get_caller()?; + let transaction_context = &invoke_context.transaction_context; + let program_id = transaction_context.get_program_key()?; let keyed_accounts = invoke_context.get_keyed_accounts()?; assert_eq!( *program_id, @@ -1242,14 +1229,14 @@ mod tests { is_writable: false, }); } - let transaction_context = TransactionContext::new(accounts, MAX_DEPTH); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(accounts, MAX_DEPTH); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); // Check call depth increases and has a limit let mut depth_reached = 0; for _ in 0..invoke_stack.len() { if Err(InstructionError::CallDepth) - == invoke_context.push(&instruction_accounts, &[MAX_DEPTH + depth_reached]) + == invoke_context.push(&instruction_accounts, &[MAX_DEPTH + depth_reached], &[]) { break; } @@ -1277,12 +1264,13 @@ mod tests { ]; // modify account owned by the program - transaction_context + invoke_context + .transaction_context .get_account_at_index(owned_index) .borrow_mut() .data_as_mut_slice()[0] = (MAX_DEPTH + owned_index) as u8; invoke_context - .verify_and_update(&instruction_accounts, None) + .verify_and_update(&instruction_accounts, false) .unwrap(); assert_eq!( invoke_context.pre_accounts[owned_index].data()[0], @@ -1290,25 +1278,28 @@ mod tests { ); // modify account not owned by the program - let data = transaction_context + let data = invoke_context + .transaction_context .get_account_at_index(not_owned_index) .borrow_mut() .data()[0]; - transaction_context + invoke_context + .transaction_context .get_account_at_index(not_owned_index) .borrow_mut() .data_as_mut_slice()[0] = (MAX_DEPTH + not_owned_index) as u8; assert_eq!( - invoke_context.verify_and_update(&instruction_accounts, None), + invoke_context.verify_and_update(&instruction_accounts, false), Err(InstructionError::ExternalAccountDataModified) ); assert_eq!(invoke_context.pre_accounts[not_owned_index].data()[0], data); - transaction_context + invoke_context + .transaction_context .get_account_at_index(not_owned_index) .borrow_mut() .data_as_mut_slice()[0] = data; - invoke_context.pop(); + invoke_context.pop().unwrap(); } } @@ -1317,20 +1308,14 @@ mod tests { let accounts = vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())]; let instruction_accounts = vec![]; let program_indices = vec![0]; - let transaction_context = TransactionContext::new(accounts, 1); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); invoke_context - .push(&instruction_accounts, &program_indices) + .push(&instruction_accounts, &program_indices, &[]) .unwrap(); assert!(invoke_context .verify(&instruction_accounts, &program_indices) .is_ok()); - - let mut _borrowed = transaction_context.get_account_at_index(0).borrow(); - assert_eq!( - invoke_context.verify(&instruction_accounts, &program_indices), - Err(InstructionError::AccountBorrowOutstanding) - ); } #[test] @@ -1367,12 +1352,15 @@ mod tests { is_writable: index_in_transaction < 2, }) .collect::>(); - let transaction_context = TransactionContext::new(accounts, 2); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, builtin_programs); + let mut transaction_context = TransactionContext::new(accounts, 2); + let mut invoke_context = + InvokeContext::new_mock(&mut transaction_context, builtin_programs); // External modification tests { - invoke_context.push(&instruction_accounts, &[4]).unwrap(); + invoke_context + .push(&instruction_accounts, &[4], &[]) + .unwrap(); let inner_instruction = Instruction::new_with_bincode( callee_program_id, &MockInstruction::NoopSuccess, @@ -1411,7 +1399,7 @@ mod tests { .borrow_mut() .data_as_mut_slice()[0] = 0; - invoke_context.pop(); + invoke_context.pop().unwrap(); } // Internal modification tests @@ -1432,18 +1420,22 @@ mod tests { ), ]; for case in cases { - invoke_context.push(&instruction_accounts, &[4]).unwrap(); + invoke_context + .push(&instruction_accounts, &[4], &[]) + .unwrap(); let inner_instruction = Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone()); assert_eq!(invoke_context.native_invoke(inner_instruction, &[]), case.1); - invoke_context.pop(); + invoke_context.pop().unwrap(); } // Compute unit consumption tests let compute_units_to_consume = 10; let expected_results = vec![Ok(()), Err(InstructionError::GenericError)]; for expected_result in expected_results { - invoke_context.push(&instruction_accounts, &[4]).unwrap(); + invoke_context + .push(&instruction_accounts, &[4], &[]) + .unwrap(); let inner_instruction = Instruction::new_with_bincode( callee_program_id, &MockInstruction::ConsumeComputeUnits { @@ -1452,16 +1444,14 @@ mod tests { }, metas.clone(), ); - let (inner_instruction_accounts, caller_write_privileges, program_indices) = - invoke_context - .prepare_instruction(&inner_instruction, &[]) - .unwrap(); + let (inner_instruction_accounts, program_indices) = invoke_context + .prepare_instruction(&inner_instruction, &[]) + .unwrap(); let mut compute_units_consumed = 0; let result = invoke_context.process_instruction( &inner_instruction.data, &inner_instruction_accounts, - Some(&caller_write_privileges), &program_indices, &mut compute_units_consumed, ); @@ -1473,7 +1463,7 @@ mod tests { assert_eq!(compute_units_consumed, compute_units_to_consume); assert_eq!(result, expected_result); - invoke_context.pop(); + invoke_context.pop().unwrap(); } } @@ -1487,18 +1477,18 @@ mod tests { let mut feature_set = FeatureSet::all_enabled(); feature_set.deactivate(&tx_wide_compute_cap::id()); feature_set.deactivate(&requestable_heap_size::id()); - let transaction_context = TransactionContext::new(accounts, 1); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); invoke_context.feature_set = Arc::new(feature_set); - invoke_context.push(&[], &[0]).unwrap(); + invoke_context.push(&[], &[0], &[]).unwrap(); assert_eq!( *invoke_context.get_compute_budget(), ComputeBudget::default() ); - invoke_context.pop(); + invoke_context.pop().unwrap(); - invoke_context.push(&[], &[1]).unwrap(); + invoke_context.push(&[], &[1], &[]).unwrap(); let expected_compute_budget = ComputeBudget { max_units: 500_000, heap_size: Some(256_usize.saturating_mul(1024)), @@ -1508,13 +1498,13 @@ mod tests { *invoke_context.get_compute_budget(), expected_compute_budget ); - invoke_context.pop(); + invoke_context.pop().unwrap(); - invoke_context.push(&[], &[0]).unwrap(); + invoke_context.push(&[], &[0], &[]).unwrap(); assert_eq!( *invoke_context.get_compute_budget(), ComputeBudget::default() ); - invoke_context.pop(); + invoke_context.pop().unwrap(); } } diff --git a/program-runtime/src/native_loader.rs b/program-runtime/src/native_loader.rs index 10b4085cc0..181387393f 100644 --- a/program-runtime/src/native_loader.rs +++ b/program-runtime/src/native_loader.rs @@ -170,7 +170,7 @@ impl NativeLoader { invoke_context: &mut InvokeContext, ) -> Result<(), InstructionError> { let (program_id, name_vec) = { - let program_id = invoke_context.get_caller()?; + let program_id = invoke_context.transaction_context.get_program_key()?; let keyed_accounts = invoke_context.get_keyed_accounts()?; let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?; if native_loader::id() != *program_id { diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index 23005e029e..6ede79c9ee 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -45,7 +45,7 @@ use { solana_vote_program::vote_state::{VoteState, VoteStateVersions}, std::{ cell::RefCell, - collections::HashMap, + collections::HashSet, convert::TryFrom, fs::File, io::{self, Read}, @@ -100,57 +100,68 @@ pub fn builtin_process_instruction( ) -> Result<(), InstructionError> { set_invoke_context(invoke_context); + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; + let indices_in_instruction = instruction_context.get_number_of_program_accounts() + ..instruction_context.get_number_of_accounts(); + let log_collector = invoke_context.get_log_collector(); - let program_id = invoke_context.get_caller()?; + let program_id = transaction_context.get_program_key()?; stable_log::program_invoke(&log_collector, program_id, invoke_context.invoke_depth()); - // Skip the processor account - let keyed_accounts = &invoke_context.get_keyed_accounts()?[1..]; + // Copy indices_in_instruction into a HashSet to ensure there are no duplicates + let deduplicated_indices: HashSet = indices_in_instruction.clone().collect(); - // Copy all the accounts into a HashMap to ensure there are no duplicates - let mut accounts: HashMap = keyed_accounts + // Create copies of the accounts + let mut account_copies = deduplicated_indices .iter() - .map(|ka| { - ( - *ka.unsigned_key(), - Account::from(ka.account.borrow().clone()), - ) + .map(|index_in_instruction| { + let borrowed_account = instruction_context + .try_borrow_account(transaction_context, *index_in_instruction)?; + Ok(( + *borrowed_account.get_key(), + *borrowed_account.get_owner(), + borrowed_account.get_lamports(), + borrowed_account.get_data().to_vec(), + )) }) - .collect(); + .collect::, InstructionError>>()?; - // Create shared references to each account's lamports/data/owner - let account_refs: HashMap<_, _> = accounts + // Create shared references to account_copies + let account_refs: Vec<_> = account_copies .iter_mut() - .map(|(key, account)| { + .map(|(key, owner, lamports, data)| { ( - *key, - ( - Rc::new(RefCell::new(&mut account.lamports)), - Rc::new(RefCell::new(&mut account.data[..])), - &account.owner, - ), + key, + owner, + Rc::new(RefCell::new(lamports)), + Rc::new(RefCell::new(data.as_mut())), ) }) .collect(); // Create AccountInfos - let account_infos: Vec = keyed_accounts - .iter() - .map(|keyed_account| { - let key = keyed_account.unsigned_key(); - let (lamports, data, owner) = &account_refs[key]; - AccountInfo { + let account_infos = indices_in_instruction + .map(|index_in_instruction| { + let account_copy_index = deduplicated_indices + .iter() + .position(|index| *index == index_in_instruction) + .unwrap(); + let (key, owner, lamports, data) = &account_refs[account_copy_index]; + let borrowed_account = instruction_context + .try_borrow_account(transaction_context, index_in_instruction)?; + Ok(AccountInfo { key, - is_signer: keyed_account.signer_key().is_some(), - is_writable: keyed_account.is_writable(), + is_signer: borrowed_account.is_signer(), + is_writable: borrowed_account.is_writable(), lamports: lamports.clone(), data: data.clone(), owner, - executable: keyed_account.executable().unwrap(), - rent_epoch: keyed_account.rent_epoch().unwrap(), - } + executable: borrowed_account.is_executable(), + rent_epoch: borrowed_account.get_rent_epoch(), + }) }) - .collect(); + .collect::, InstructionError>>()?; // Execute the program process_instruction(program_id, &account_infos, input).map_err(|err| { @@ -161,12 +172,16 @@ pub fn builtin_process_instruction( stable_log::program_success(&log_collector, program_id); // Commit AccountInfo changes back into KeyedAccounts - for keyed_account in keyed_accounts { - let mut account = keyed_account.account.borrow_mut(); - let key = keyed_account.unsigned_key(); - let (lamports, data, _owner) = &account_refs[key]; - account.set_lamports(**lamports.borrow()); - account.set_data(data.borrow().to_vec()); + for (index_in_instruction, (_key, _owner, lamports, data)) in deduplicated_indices + .into_iter() + .zip(account_copies.into_iter()) + { + let mut borrowed_account = + instruction_context.try_borrow_account(transaction_context, index_in_instruction)?; + if borrowed_account.is_writable() { + borrowed_account.set_lamports(lamports)?; + borrowed_account.set_data(&data)?; + } } Ok(()) @@ -233,7 +248,10 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { let invoke_context = get_invoke_context(); let log_collector = invoke_context.get_log_collector(); - let caller = *invoke_context.get_caller().expect("get_caller"); + let caller = *invoke_context + .transaction_context + .get_program_key() + .unwrap(); stable_log::program_invoke( &log_collector, @@ -245,7 +263,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { .iter() .map(|seeds| Pubkey::create_program_address(seeds, &caller).unwrap()) .collect::>(); - let (instruction_accounts, caller_write_privileges, program_indices) = invoke_context + let (instruction_accounts, program_indices) = invoke_context .prepare_instruction(instruction, &signers) .unwrap(); @@ -281,7 +299,6 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { .process_instruction( &instruction.data, &instruction_accounts, - Some(&caller_write_privileges), &program_indices, &mut compute_units_consumed, ) diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index 63513f6a77..859cf171d3 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -209,12 +209,9 @@ fn bench_create_vm(bencher: &mut Bencher) { .mock_set_remaining(BUDGET); // Serialize account data - let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); let (mut serialized, account_lengths) = serialize_parameters( - &keyed_accounts[0].unsigned_key(), - &keyed_accounts[1].unsigned_key(), - &keyed_accounts[2..], - &[], + invoke_context.transaction_context, + invoke_context.transaction_context.get_current_instruction_context().unwrap(), ) .unwrap(); @@ -250,12 +247,9 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) { .mock_set_remaining(BUDGET); // Serialize account data - let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); let (mut serialized, account_lengths) = serialize_parameters( - &keyed_accounts[0].unsigned_key(), - &keyed_accounts[1].unsigned_key(), - &keyed_accounts[2..], - &[], + invoke_context.transaction_context, + invoke_context.transaction_context.get_current_instruction_context().unwrap(), ) .unwrap(); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index a5fc4afdda..55a0d38490 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -196,12 +196,9 @@ fn run_program(name: &str) -> u64 { file.read_to_end(&mut data).unwrap(); let loader_id = bpf_loader::id(); with_mock_invoke_context(loader_id, 0, |invoke_context| { - let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); let (parameter_bytes, account_lengths) = serialize_parameters( - &keyed_accounts[0].unsigned_key(), - &keyed_accounts[1].unsigned_key(), - &keyed_accounts[2..], - &[], + invoke_context.transaction_context, + invoke_context.transaction_context.get_current_instruction_context().unwrap(), ) .unwrap(); @@ -227,7 +224,7 @@ fn run_program(name: &str) -> u64 { let mut instruction_count = 0; let mut tracer = None; for i in 0..2 { - invoke_context.return_data = (*invoke_context.get_caller().unwrap(), Vec::new()); + invoke_context.return_data = (*invoke_context.transaction_context.get_program_key().unwrap(), Vec::new()); let mut parameter_bytes = parameter_bytes.clone(); { let mut vm = create_vm( @@ -278,10 +275,9 @@ fn run_program(name: &str) -> u64 { tracer = Some(vm.get_tracer().clone()); } } - let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); deserialize_parameters( - &loader_id, - &keyed_accounts[2..], + invoke_context.transaction_context, + invoke_context.transaction_context.get_current_instruction_context().unwrap(), parameter_bytes.as_slice(), &account_lengths, true, diff --git a/programs/bpf_loader/benches/serialization.rs b/programs/bpf_loader/benches/serialization.rs index 1287f518ee..900f39df8e 100644 --- a/programs/bpf_loader/benches/serialization.rs +++ b/programs/bpf_loader/benches/serialization.rs @@ -9,136 +9,123 @@ use { solana_sdk::{ account::{Account, AccountSharedData}, bpf_loader, - keyed_account::KeyedAccount, - pubkey::Pubkey, + transaction_context::{InstructionAccount, TransactionContext}, }, - std::cell::RefCell, test::Bencher, }; -fn create_inputs() -> ( - Pubkey, - Vec, - Vec>, - Vec, -) { +fn create_inputs() -> TransactionContext { let program_id = solana_sdk::pubkey::new_rand(); - let dup_key = solana_sdk::pubkey::new_rand(); - let dup_key2 = solana_sdk::pubkey::new_rand(); - let keys = vec![ - dup_key, - dup_key, - solana_sdk::pubkey::new_rand(), - solana_sdk::pubkey::new_rand(), - dup_key2, - dup_key2, - solana_sdk::pubkey::new_rand(), - solana_sdk::pubkey::new_rand(), + let transaction_accounts = vec![ + ( + program_id, + AccountSharedData::from(Account { + lamports: 0, + data: vec![], + owner: bpf_loader::id(), + executable: true, + rent_epoch: 0, + }), + ), + ( + solana_sdk::pubkey::new_rand(), + AccountSharedData::from(Account { + lamports: 1, + data: vec![1u8; 100000], + owner: bpf_loader::id(), + executable: false, + rent_epoch: 100, + }), + ), + ( + solana_sdk::pubkey::new_rand(), + AccountSharedData::from(Account { + lamports: 2, + data: vec![11u8; 100000], + owner: bpf_loader::id(), + executable: true, + rent_epoch: 200, + }), + ), + ( + solana_sdk::pubkey::new_rand(), + AccountSharedData::from(Account { + lamports: 3, + data: vec![], + owner: bpf_loader::id(), + executable: false, + rent_epoch: 3100, + }), + ), + ( + solana_sdk::pubkey::new_rand(), + AccountSharedData::from(Account { + lamports: 4, + data: vec![1u8; 100000], + owner: bpf_loader::id(), + executable: false, + rent_epoch: 100, + }), + ), + ( + solana_sdk::pubkey::new_rand(), + AccountSharedData::from(Account { + lamports: 5, + data: vec![11u8; 10000], + owner: bpf_loader::id(), + executable: true, + rent_epoch: 200, + }), + ), + ( + solana_sdk::pubkey::new_rand(), + AccountSharedData::from(Account { + lamports: 6, + data: vec![], + owner: bpf_loader::id(), + executable: false, + rent_epoch: 3100, + }), + ), ]; - let accounts = vec![ - RefCell::new(AccountSharedData::from(Account { - lamports: 1, - data: vec![1u8, 2, 3, 4, 5], - owner: bpf_loader::id(), - executable: false, - rent_epoch: 100, - })), - // dup - RefCell::new(AccountSharedData::from(Account { - lamports: 1, - data: vec![1u8; 100000], - owner: bpf_loader::id(), - executable: false, - rent_epoch: 100, - })), - RefCell::new(AccountSharedData::from(Account { - lamports: 2, - data: vec![11u8; 100000], - owner: bpf_loader::id(), - executable: true, - rent_epoch: 200, - })), - RefCell::new(AccountSharedData::from(Account { - lamports: 3, - data: vec![], - owner: bpf_loader::id(), - executable: false, - rent_epoch: 3100, - })), - RefCell::new(AccountSharedData::from(Account { - lamports: 4, - data: vec![1u8; 100000], - owner: bpf_loader::id(), - executable: false, - rent_epoch: 100, - })), - // dup - RefCell::new(AccountSharedData::from(Account { - lamports: 4, - data: vec![1u8; 1000000], - owner: bpf_loader::id(), - executable: false, - rent_epoch: 100, - })), - RefCell::new(AccountSharedData::from(Account { - lamports: 5, - data: vec![11u8; 10000], - owner: bpf_loader::id(), - executable: true, - rent_epoch: 200, - })), - RefCell::new(AccountSharedData::from(Account { - lamports: 6, - data: vec![], - owner: bpf_loader::id(), - executable: false, - rent_epoch: 3100, - })), - ]; - + let instruction_accounts = [1, 1, 2, 3, 4, 4, 5, 6] + .into_iter() + .enumerate() + .map( + |(index_in_instruction, index_in_transaction)| InstructionAccount { + index_in_caller: 1usize.saturating_add(index_in_instruction), + index_in_transaction, + is_signer: false, + is_writable: index_in_instruction >= 4, + }, + ) + .collect::>(); + let mut transaction_context = TransactionContext::new(transaction_accounts, 1); let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; - - (program_id, keys, accounts, instruction_data) + transaction_context + .push(&[0], &instruction_accounts, &instruction_data) + .unwrap(); + transaction_context } #[bench] fn bench_serialize_unaligned(bencher: &mut Bencher) { - let (program_id, keys, accounts, instruction_data) = create_inputs(); - let keyed_accounts: Vec<_> = keys - .iter() - .zip(&accounts) - .enumerate() - .map(|(i, (key, account))| { - if i <= accounts.len() / 2 { - KeyedAccount::new_readonly(key, false, account) - } else { - KeyedAccount::new(key, false, account) - } - }) - .collect(); + let transaction_context = create_inputs(); + let instruction_context = transaction_context + .get_current_instruction_context() + .unwrap(); bencher.iter(|| { - let _ = serialize_parameters_unaligned(&program_id, &keyed_accounts, &instruction_data) - .unwrap(); + let _ = serialize_parameters_unaligned(&transaction_context, instruction_context).unwrap(); }); } #[bench] fn bench_serialize_aligned(bencher: &mut Bencher) { - let (program_id, keys, accounts, instruction_data) = create_inputs(); - let keyed_accounts: Vec<_> = keys - .iter() - .zip(&accounts) - .enumerate() - .map(|(i, (key, account))| { - if i <= accounts.len() / 2 { - KeyedAccount::new_readonly(key, false, account) - } else { - KeyedAccount::new(key, false, account) - } - }) - .collect(); + let transaction_context = create_inputs(); + let instruction_context = transaction_context + .get_current_instruction_context() + .unwrap(); bencher.iter(|| { - let _ = - serialize_parameters_aligned(&program_id, &keyed_accounts, &instruction_data).unwrap(); + let _ = serialize_parameters_aligned(&transaction_context, instruction_context).unwrap(); }); } diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index d301fc2e7a..118f71657f 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -225,7 +225,7 @@ fn process_instruction_common( use_jit: bool, ) -> Result<(), InstructionError> { let log_collector = invoke_context.get_log_collector(); - let program_id = invoke_context.get_caller()?; + let program_id = invoke_context.transaction_context.get_program_key()?; let keyed_accounts = invoke_context.get_keyed_accounts()?; let first_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; @@ -301,7 +301,7 @@ fn process_instruction_common( use_jit, false, )?; - let program_id = invoke_context.get_caller()?; + let program_id = invoke_context.transaction_context.get_program_key()?; invoke_context.add_executor(program_id, executor.clone()); executor } @@ -342,7 +342,7 @@ fn process_loader_upgradeable_instruction( use_jit: bool, ) -> Result<(), InstructionError> { let log_collector = invoke_context.get_log_collector(); - let program_id = invoke_context.get_caller()?; + let program_id = invoke_context.transaction_context.get_program_key()?; let keyed_accounts = invoke_context.get_keyed_accounts()?; match limited_deserialize(instruction_data)? { @@ -495,7 +495,7 @@ fn process_loader_upgradeable_instruction( .accounts .push(AccountMeta::new(*buffer.unsigned_key(), false)); - let caller_program_id = invoke_context.get_caller()?; + let caller_program_id = invoke_context.transaction_context.get_program_key()?; let signers = [&[new_program_id.as_ref(), &[bump_seed]]] .iter() .map(|seeds| Pubkey::create_program_address(*seeds, caller_program_id)) @@ -892,7 +892,7 @@ fn process_loader_instruction( invoke_context: &mut InvokeContext, use_jit: bool, ) -> Result<(), InstructionError> { - let program_id = invoke_context.get_caller()?; + let program_id = invoke_context.transaction_context.get_program_key()?; let keyed_accounts = invoke_context.get_keyed_accounts()?; let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?; if program.owner()? != *program_id { @@ -973,8 +973,8 @@ impl Debug for BpfExecutor { impl Executor for BpfExecutor { fn execute<'a, 'b>( &self, - first_instruction_account: usize, - instruction_data: &[u8], + _first_instruction_account: usize, + _instruction_data: &[u8], invoke_context: &'a mut InvokeContext<'b>, use_jit: bool, ) -> Result<(), InstructionError> { @@ -983,15 +983,12 @@ impl Executor for BpfExecutor { let invoke_depth = invoke_context.invoke_depth(); let mut serialize_time = Measure::start("serialize"); - let keyed_accounts = invoke_context.get_keyed_accounts()?; - let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?; - let loader_id = program.owner()?; - let program_id = *program.unsigned_key(); + let program_id = *invoke_context.transaction_context.get_program_key()?; let (mut parameter_bytes, account_lengths) = serialize_parameters( - &loader_id, - &program_id, - &keyed_accounts[first_instruction_account + 1..], - instruction_data, + invoke_context.transaction_context, + invoke_context + .transaction_context + .get_current_instruction_context()?, )?; serialize_time.stop(); let mut create_vm_time = Measure::start("create_vm"); @@ -1076,10 +1073,11 @@ impl Executor for BpfExecutor { let mut deserialize_time = Measure::start("deserialize"); let execute_or_deserialize_result = execution_result.and_then(|_| { - let keyed_accounts = invoke_context.get_keyed_accounts()?; deserialize_parameters( - &loader_id, - &keyed_accounts[first_instruction_account + 1..], + invoke_context.transaction_context, + invoke_context + .transaction_context + .get_current_instruction_context()?, parameter_bytes.as_slice(), &account_lengths, invoke_context diff --git a/programs/bpf_loader/src/serialization.rs b/programs/bpf_loader/src/serialization.rs index db6c4297d3..cc4c796993 100644 --- a/programs/bpf_loader/src/serialization.rs +++ b/programs/bpf_loader/src/serialization.rs @@ -2,58 +2,79 @@ use { byteorder::{ByteOrder, LittleEndian, WriteBytesExt}, solana_rbpf::{aligned_memory::AlignedMemory, ebpf::HOST_ALIGN}, solana_sdk::{ - account::{ReadableAccount, WritableAccount}, bpf_loader_deprecated, entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE}, instruction::InstructionError, keyed_account::KeyedAccount, pubkey::Pubkey, system_instruction::MAX_PERMITTED_DATA_LENGTH, + transaction_context::{InstructionContext, TransactionContext}, }, std::{io::prelude::*, mem::size_of}, }; /// Look for a duplicate account and return its position if found -pub fn is_dup(accounts: &[KeyedAccount], keyed_account: &KeyedAccount) -> (bool, usize) { - for (i, account) in accounts.iter().enumerate() { - if account == keyed_account { - return (true, i); - } - } - (false, 0) +pub fn is_duplicate( + instruction_context: &InstructionContext, + index_in_instruction: usize, +) -> Option { + let index_in_transaction = instruction_context.get_index_in_transaction(index_in_instruction); + (instruction_context.get_number_of_program_accounts()..index_in_instruction).position( + |index_in_instruction| { + instruction_context.get_index_in_transaction(index_in_instruction) + == index_in_transaction + }, + ) } pub fn serialize_parameters( - loader_id: &Pubkey, - program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], - data: &[u8], + transaction_context: &TransactionContext, + instruction_context: &InstructionContext, ) -> Result<(AlignedMemory, Vec), InstructionError> { - if *loader_id == bpf_loader_deprecated::id() { - serialize_parameters_unaligned(program_id, keyed_accounts, data) + let is_loader_deprecated = *instruction_context + .try_borrow_program_account(transaction_context)? + .get_owner() + == bpf_loader_deprecated::id(); + if is_loader_deprecated { + serialize_parameters_unaligned(transaction_context, instruction_context) } else { - serialize_parameters_aligned(program_id, keyed_accounts, data) + serialize_parameters_aligned(transaction_context, instruction_context) } .and_then(|buffer| { - let account_lengths = keyed_accounts - .iter() - .map(|keyed_account| keyed_account.data_len()) + let account_lengths = (instruction_context.get_number_of_program_accounts() + ..instruction_context.get_number_of_accounts()) + .map(|index_in_instruction| { + Ok(instruction_context + .try_borrow_account(transaction_context, index_in_instruction)? + .get_data() + .len()) + }) .collect::, InstructionError>>()?; Ok((buffer, account_lengths)) }) } pub fn deserialize_parameters( - loader_id: &Pubkey, - keyed_accounts: &[KeyedAccount], + transaction_context: &TransactionContext, + instruction_context: &InstructionContext, buffer: &[u8], account_lengths: &[usize], do_support_realloc: bool, ) -> Result<(), InstructionError> { - if *loader_id == bpf_loader_deprecated::id() { - deserialize_parameters_unaligned(keyed_accounts, buffer, account_lengths) + let is_loader_deprecated = *instruction_context + .try_borrow_program_account(transaction_context)? + .get_owner() + == bpf_loader_deprecated::id(); + if is_loader_deprecated { + deserialize_parameters_unaligned(transaction_context, instruction_context, buffer) } else { - deserialize_parameters_aligned(keyed_accounts, buffer, account_lengths, do_support_realloc) + deserialize_parameters_aligned( + transaction_context, + instruction_context, + buffer, + account_lengths, + do_support_realloc, + ) } } @@ -75,90 +96,108 @@ pub fn get_serialized_account_size_unaligned( } pub fn serialize_parameters_unaligned( - program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], - instruction_data: &[u8], + transaction_context: &TransactionContext, + instruction_context: &InstructionContext, ) -> Result { // Calculate size in order to alloc once let mut size = size_of::(); - for (i, keyed_account) in keyed_accounts.iter().enumerate() { - let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); + for index_in_instruction in instruction_context.get_number_of_program_accounts() + ..instruction_context.get_number_of_accounts() + { + let duplicate = is_duplicate(instruction_context, index_in_instruction); size += 1; // dup - if !is_dup { - size += get_serialized_account_size_unaligned(keyed_account)?; + if duplicate.is_none() { + let data_len = instruction_context + .try_borrow_account(transaction_context, index_in_instruction)? + .get_data() + .len(); + size += size_of::() // is_signer + + size_of::() // is_writable + + size_of::() // key + + size_of::() // lamports + + size_of::() // data len + + data_len // data + + size_of::() // owner + + size_of::() // executable + + size_of::(); // rent_epoch } } size += size_of::() // instruction data len - + instruction_data.len() // instruction data + + instruction_context.get_instruction_data().len() // instruction data + size_of::(); // program id let mut v = AlignedMemory::new(size, HOST_ALIGN); - v.write_u64::(keyed_accounts.len() as u64) + v.write_u64::(instruction_context.get_number_of_instruction_accounts() as u64) .map_err(|_| InstructionError::InvalidArgument)?; - for (i, keyed_account) in keyed_accounts.iter().enumerate() { - let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account); - if is_dup { + for index_in_instruction in instruction_context.get_number_of_program_accounts() + ..instruction_context.get_number_of_accounts() + { + let duplicate = is_duplicate(instruction_context, index_in_instruction); + if let Some(position) = duplicate { v.write_u8(position as u8) .map_err(|_| InstructionError::InvalidArgument)?; } else { + let borrowed_account = instruction_context + .try_borrow_account(transaction_context, index_in_instruction)?; v.write_u8(std::u8::MAX) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u8(keyed_account.signer_key().is_some() as u8) + v.write_u8(borrowed_account.is_signer() as u8) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u8(keyed_account.is_writable() as u8) + v.write_u8(borrowed_account.is_writable() as u8) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_all(keyed_account.unsigned_key().as_ref()) + v.write_all(borrowed_account.get_key().as_ref()) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u64::(keyed_account.lamports()?) + v.write_u64::(borrowed_account.get_lamports()) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u64::(keyed_account.data_len()? as u64) + v.write_u64::(borrowed_account.get_data().len() as u64) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_all(keyed_account.try_account_ref()?.data()) + v.write_all(borrowed_account.get_data()) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_all(keyed_account.owner()?.as_ref()) + v.write_all(borrowed_account.get_owner().as_ref()) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u8(keyed_account.executable()? as u8) + v.write_u8(borrowed_account.is_executable() as u8) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u64::(keyed_account.rent_epoch()? as u64) + v.write_u64::(borrowed_account.get_rent_epoch() as u64) .map_err(|_| InstructionError::InvalidArgument)?; } } - v.write_u64::(instruction_data.len() as u64) + v.write_u64::(instruction_context.get_instruction_data().len() as u64) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_all(instruction_data) - .map_err(|_| InstructionError::InvalidArgument)?; - v.write_all(program_id.as_ref()) + v.write_all(instruction_context.get_instruction_data()) .map_err(|_| InstructionError::InvalidArgument)?; + v.write_all( + instruction_context + .try_borrow_program_account(transaction_context)? + .get_key() + .as_ref(), + ) + .map_err(|_| InstructionError::InvalidArgument)?; Ok(v) } pub fn deserialize_parameters_unaligned( - keyed_accounts: &[KeyedAccount], + transaction_context: &TransactionContext, + instruction_context: &InstructionContext, buffer: &[u8], - account_lengths: &[usize], ) -> Result<(), InstructionError> { let mut start = size_of::(); // number of accounts - for (i, (keyed_account, _pre_len)) in keyed_accounts - .iter() - .zip(account_lengths.iter()) - .enumerate() + for index_in_instruction in instruction_context.get_number_of_program_accounts() + ..instruction_context.get_number_of_accounts() { - let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); + let duplicate = is_duplicate(instruction_context, index_in_instruction); start += 1; // is_dup - if !is_dup { + if duplicate.is_none() { + let mut borrowed_account = instruction_context + .try_borrow_account(transaction_context, index_in_instruction)?; start += size_of::(); // is_signer start += size_of::(); // is_writable start += size_of::(); // key - keyed_account - .try_account_ref_mut()? - .set_lamports(LittleEndian::read_u64(&buffer[start..])); + let _ = borrowed_account.set_lamports(LittleEndian::read_u64(&buffer[start..])); start += size_of::() // lamports + size_of::(); // data length - let end = start + keyed_account.data_len()?; - keyed_account - .try_account_ref_mut()? - .set_data_from_slice(&buffer[start..end]); - start += keyed_account.data_len()? // data + let end = start + borrowed_account.get_data().len(); + let _ = borrowed_account.set_data(&buffer[start..end]); + start += borrowed_account.get_data().len() // data + size_of::() // owner + size_of::() // executable + size_of::(); // rent_epoch @@ -167,77 +206,77 @@ pub fn deserialize_parameters_unaligned( Ok(()) } -pub fn get_serialized_account_size_aligned( - keyed_account: &KeyedAccount, -) -> Result { - let data_len = keyed_account.data_len()?; - Ok( - size_of::() // is_signer - + size_of::() // is_writable - + size_of::() // executable - + 4 // padding to 128-bit aligned - + size_of::() // key - + size_of::() // owner - + size_of::() // lamports - + size_of::() // data len - + data_len - + MAX_PERMITTED_DATA_INCREASE - + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128) - + size_of::(), // rent epoch - ) -} - pub fn serialize_parameters_aligned( - program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], - instruction_data: &[u8], + transaction_context: &TransactionContext, + instruction_context: &InstructionContext, ) -> Result { // Calculate size in order to alloc once let mut size = size_of::(); - for (i, keyed_account) in keyed_accounts.iter().enumerate() { - let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); + for index_in_instruction in instruction_context.get_number_of_program_accounts() + ..instruction_context.get_number_of_accounts() + { + let duplicate = is_duplicate(instruction_context, index_in_instruction); size += 1; // dup - if is_dup { + if duplicate.is_some() { size += 7; // padding to 64-bit aligned } else { - size += get_serialized_account_size_aligned(keyed_account)?; + let data_len = instruction_context + .try_borrow_account(transaction_context, index_in_instruction)? + .get_data() + .len(); + size += size_of::() // is_signer + + size_of::() // is_writable + + size_of::() // executable + + 4 // padding to 128-bit aligned + + size_of::() // key + + size_of::() // owner + + size_of::() // lamports + + size_of::() // data len + + data_len + + MAX_PERMITTED_DATA_INCREASE + + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128) + + size_of::(); // rent epoch } } size += size_of::() // data len - + instruction_data.len() + + instruction_context.get_instruction_data().len() + size_of::(); // program id; let mut v = AlignedMemory::new(size, HOST_ALIGN); // Serialize into the buffer - v.write_u64::(keyed_accounts.len() as u64) + v.write_u64::(instruction_context.get_number_of_instruction_accounts() as u64) .map_err(|_| InstructionError::InvalidArgument)?; - for (i, keyed_account) in keyed_accounts.iter().enumerate() { - let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account); - if is_dup { + for index_in_instruction in instruction_context.get_number_of_program_accounts() + ..instruction_context.get_number_of_accounts() + { + let duplicate = is_duplicate(instruction_context, index_in_instruction); + if let Some(position) = duplicate { v.write_u8(position as u8) .map_err(|_| InstructionError::InvalidArgument)?; v.write_all(&[0u8, 0, 0, 0, 0, 0, 0]) .map_err(|_| InstructionError::InvalidArgument)?; // 7 bytes of padding to make 64-bit aligned } else { + let borrowed_account = instruction_context + .try_borrow_account(transaction_context, index_in_instruction)?; v.write_u8(std::u8::MAX) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u8(keyed_account.signer_key().is_some() as u8) + v.write_u8(borrowed_account.is_signer() as u8) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u8(keyed_account.is_writable() as u8) + v.write_u8(borrowed_account.is_writable() as u8) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u8(keyed_account.executable()? as u8) + v.write_u8(borrowed_account.is_executable() as u8) .map_err(|_| InstructionError::InvalidArgument)?; v.write_all(&[0u8, 0, 0, 0]) .map_err(|_| InstructionError::InvalidArgument)?; // 4 bytes of padding to make 128-bit aligned - v.write_all(keyed_account.unsigned_key().as_ref()) + v.write_all(borrowed_account.get_key().as_ref()) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_all(keyed_account.owner()?.as_ref()) + v.write_all(borrowed_account.get_owner().as_ref()) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u64::(keyed_account.lamports()?) + v.write_u64::(borrowed_account.get_lamports()) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u64::(keyed_account.data_len()? as u64) + v.write_u64::(borrowed_account.get_data().len() as u64) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_all(keyed_account.try_account_ref()?.data()) + v.write_all(borrowed_account.get_data()) .map_err(|_| InstructionError::InvalidArgument)?; v.resize( MAX_PERMITTED_DATA_INCREASE @@ -245,45 +284,51 @@ pub fn serialize_parameters_aligned( 0, ) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_u64::(keyed_account.rent_epoch()? as u64) + v.write_u64::(borrowed_account.get_rent_epoch() as u64) .map_err(|_| InstructionError::InvalidArgument)?; } } - v.write_u64::(instruction_data.len() as u64) + v.write_u64::(instruction_context.get_instruction_data().len() as u64) .map_err(|_| InstructionError::InvalidArgument)?; - v.write_all(instruction_data) - .map_err(|_| InstructionError::InvalidArgument)?; - v.write_all(program_id.as_ref()) + v.write_all(instruction_context.get_instruction_data()) .map_err(|_| InstructionError::InvalidArgument)?; + v.write_all( + instruction_context + .try_borrow_program_account(transaction_context)? + .get_key() + .as_ref(), + ) + .map_err(|_| InstructionError::InvalidArgument)?; Ok(v) } pub fn deserialize_parameters_aligned( - keyed_accounts: &[KeyedAccount], + transaction_context: &TransactionContext, + instruction_context: &InstructionContext, buffer: &[u8], account_lengths: &[usize], do_support_realloc: bool, ) -> Result<(), InstructionError> { let mut start = size_of::(); // number of accounts - for (i, (keyed_account, pre_len)) in keyed_accounts - .iter() + for (index_in_instruction, pre_len) in (instruction_context.get_number_of_program_accounts() + ..instruction_context.get_number_of_accounts()) .zip(account_lengths.iter()) - .enumerate() { - let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); + let duplicate = is_duplicate(instruction_context, index_in_instruction); start += size_of::(); // position - if is_dup { + if duplicate.is_some() { start += 7; // padding to 64-bit aligned } else { - let mut account = keyed_account.try_account_ref_mut()?; + let mut borrowed_account = instruction_context + .try_borrow_account(transaction_context, index_in_instruction)?; start += size_of::() // is_signer + size_of::() // is_writable + size_of::() // executable + 4 // padding to 128-bit aligned + size_of::(); // key - account.copy_into_owner_from_slice(&buffer[start..start + size_of::()]); + let _ = borrowed_account.set_owner(&buffer[start..start + size_of::()]); start += size_of::(); // owner - account.set_lamports(LittleEndian::read_u64(&buffer[start..])); + let _ = borrowed_account.set_lamports(LittleEndian::read_u64(&buffer[start..])); start += size_of::(); // lamports let post_len = LittleEndian::read_u64(&buffer[start..]) as usize; start += size_of::(); // data length @@ -303,7 +348,7 @@ pub fn deserialize_parameters_aligned( } data_end }; - account.set_data_from_slice(&buffer[start..data_end]); + let _ = borrowed_account.set_data(&buffer[start..data_end]); start += *pre_len + MAX_PERMITTED_DATA_INCREASE; // data start += (start as *const u8).align_offset(BPF_ALIGN_OF_U128); start += size_of::(); // rent_epoch @@ -318,12 +363,11 @@ mod tests { super::*, solana_program_runtime::invoke_context::{prepare_mock_invoke_context, InvokeContext}, solana_sdk::{ - account::{Account, AccountSharedData}, + account::{Account, AccountSharedData, ReadableAccount}, account_info::AccountInfo, bpf_loader, entrypoint::deserialize, instruction::AccountMeta, - transaction_context::TransactionContext, }, std::{ cell::RefCell, @@ -407,71 +451,40 @@ mod tests { }), ), ]; - let instruction_accounts = vec![ - AccountMeta { - pubkey: transaction_accounts[1].0, + let instruction_accounts = [1, 1, 2, 3, 4, 4, 5, 6] + .into_iter() + .enumerate() + .map(|(index_in_instruction, index_in_transaction)| AccountMeta { + pubkey: transaction_accounts[index_in_transaction].0, is_signer: false, - is_writable: false, - }, - AccountMeta { - pubkey: transaction_accounts[1].0, - is_signer: false, - is_writable: false, - }, - AccountMeta { - pubkey: transaction_accounts[2].0, - is_signer: false, - is_writable: false, - }, - AccountMeta { - pubkey: transaction_accounts[3].0, - is_signer: false, - is_writable: false, - }, - AccountMeta { - pubkey: transaction_accounts[4].0, - is_signer: false, - is_writable: true, - }, - AccountMeta { - pubkey: transaction_accounts[4].0, - is_signer: false, - is_writable: true, - }, - AccountMeta { - pubkey: transaction_accounts[5].0, - is_signer: false, - is_writable: true, - }, - AccountMeta { - pubkey: transaction_accounts[6].0, - is_signer: false, - is_writable: true, - }, - ]; + is_writable: index_in_instruction >= 4, + }) + .collect(); let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; let program_indices = [0]; let preparation = prepare_mock_invoke_context( - transaction_accounts.clone(), + transaction_accounts, instruction_accounts, &program_indices, ); - let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(preparation.transaction_accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); invoke_context - .push(&preparation.instruction_accounts, &program_indices) + .push( + &preparation.instruction_accounts, + &program_indices, + &instruction_data, + ) + .unwrap(); + let instruction_context = invoke_context + .transaction_context + .get_current_instruction_context() .unwrap(); // check serialize_parameters_aligned - let ser_keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); - let (mut serialized, account_lengths) = serialize_parameters( - &bpf_loader::id(), - &program_id, - &ser_keyed_accounts[1..], - &instruction_data, - ) - .unwrap(); + let (mut serialized, account_lengths) = + serialize_parameters(invoke_context.transaction_context, instruction_context).unwrap(); let (de_program_id, de_accounts, de_instruction_data) = unsafe { deserialize(&mut serialized.as_slice_mut()[0] as *mut u8) }; @@ -483,11 +496,14 @@ mod tests { 0 ); for account_info in de_accounts { - let account = &transaction_accounts - .iter() - .find(|(key, _account)| key == account_info.key) - .unwrap() - .1; + let index_in_transaction = invoke_context + .transaction_context + .find_index_of_account(account_info.key) + .unwrap(); + let account = invoke_context + .transaction_context + .get_account_at_index(index_in_transaction) + .borrow(); assert_eq!(account.lamports(), account_info.lamports()); assert_eq!(account.data(), &account_info.data.borrow()[..]); assert_eq!(account.owner(), account_info.owner); @@ -510,44 +526,51 @@ mod tests { let de_keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); deserialize_parameters( - &bpf_loader::id(), - &de_keyed_accounts[1..], + invoke_context.transaction_context, + instruction_context, serialized.as_slice(), &account_lengths, true, ) .unwrap(); for keyed_account in de_keyed_accounts { - let account = &transaction_accounts - .iter() - .find(|(key, _account)| key == keyed_account.unsigned_key()) - .unwrap() - .1; + let index_in_transaction = invoke_context + .transaction_context + .find_index_of_account(keyed_account.unsigned_key()) + .unwrap(); + let account = invoke_context + .transaction_context + .get_account_at_index(index_in_transaction) + .borrow(); assert_eq!(account.executable(), keyed_account.executable().unwrap()); assert_eq!(account.rent_epoch(), keyed_account.rent_epoch().unwrap()); } // check serialize_parameters_unaligned + let _ = invoke_context + .transaction_context + .get_current_instruction_context() + .unwrap() + .try_borrow_account(invoke_context.transaction_context, 0) + .unwrap() + .set_owner(bpf_loader_deprecated::id().as_ref()); - let ser_keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); - let (mut serialized, account_lengths) = serialize_parameters( - &bpf_loader_deprecated::id(), - &program_id, - &ser_keyed_accounts[1..], - &instruction_data, - ) - .unwrap(); + let (mut serialized, account_lengths) = + serialize_parameters(invoke_context.transaction_context, instruction_context).unwrap(); let (de_program_id, de_accounts, de_instruction_data) = unsafe { deserialize_unaligned(&mut serialized.as_slice_mut()[0] as *mut u8) }; assert_eq!(&program_id, de_program_id); assert_eq!(instruction_data, de_instruction_data); for account_info in de_accounts { - let account = &transaction_accounts - .iter() - .find(|(key, _account)| key == account_info.key) - .unwrap() - .1; + let index_in_transaction = invoke_context + .transaction_context + .find_index_of_account(account_info.key) + .unwrap(); + let account = invoke_context + .transaction_context + .get_account_at_index(index_in_transaction) + .borrow(); assert_eq!(account.lamports(), account_info.lamports()); assert_eq!(account.data(), &account_info.data.borrow()[..]); assert_eq!(account.owner(), account_info.owner); @@ -557,19 +580,22 @@ mod tests { let de_keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); deserialize_parameters( - &bpf_loader_deprecated::id(), - &de_keyed_accounts[1..], + invoke_context.transaction_context, + instruction_context, serialized.as_slice(), &account_lengths, true, ) .unwrap(); for keyed_account in de_keyed_accounts { - let account = &transaction_accounts - .iter() - .find(|(key, _account)| key == keyed_account.unsigned_key()) - .unwrap() - .1; + let index_in_transaction = invoke_context + .transaction_context + .find_index_of_account(keyed_account.unsigned_key()) + .unwrap(); + let account = invoke_context + .transaction_context + .get_account_at_index(index_in_transaction) + .borrow(); assert_eq!(account.lamports(), keyed_account.lamports().unwrap()); assert_eq!( account.data(), diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index e04640f570..1ef16a5284 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -246,7 +246,8 @@ pub fn bind_syscall_context_objects<'a, 'b>( .is_active(&sol_log_data_syscall_enabled::id()); let loader_id = invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError)?; let invoke_context = Rc::new(RefCell::new(invoke_context)); @@ -581,7 +582,8 @@ impl<'a, 'b> SyscallObject for SyscallPanic<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -620,7 +622,8 @@ impl<'a, 'b> SyscallObject for SyscallLog<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -734,7 +737,8 @@ impl<'a, 'b> SyscallObject for SyscallLogPubkey<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -850,7 +854,8 @@ impl<'a, 'b> SyscallObject for SyscallCreateProgramAddress<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -909,7 +914,8 @@ impl<'a, 'b> SyscallObject for SyscallTryFindProgramAddress<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -983,7 +989,8 @@ impl<'a, 'b> SyscallObject for SyscallSha256<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1064,7 +1071,8 @@ impl<'a, 'b> SyscallObject for SyscallGetClockSysvar<'a, 'b> { ); let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1100,7 +1108,8 @@ impl<'a, 'b> SyscallObject for SyscallGetEpochScheduleSysvar<'a, 'b> { ); let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1137,7 +1146,8 @@ impl<'a, 'b> SyscallObject for SyscallGetFeesSysvar<'a, 'b> { ); let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1173,7 +1183,8 @@ impl<'a, 'b> SyscallObject for SyscallGetRentSysvar<'a, 'b> { ); let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1216,7 +1227,8 @@ impl<'a, 'b> SyscallObject for SyscallKeccak256<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1316,7 +1328,8 @@ impl<'a, 'b> SyscallObject for SyscallMemcpy<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1360,7 +1373,8 @@ impl<'a, 'b> SyscallObject for SyscallMemmove<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1404,7 +1418,8 @@ impl<'a, 'b> SyscallObject for SyscallMemcmp<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1461,7 +1476,8 @@ impl<'a, 'b> SyscallObject for SyscallMemset<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1503,7 +1519,8 @@ impl<'a, 'b> SyscallObject for SyscallSecp256k1Recover<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -1608,7 +1625,8 @@ impl<'a, 'b> SyscallObject for SyscallBlake3<'a, 'b> { let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -2202,14 +2220,10 @@ fn get_translated_accounts<'a, T, F>( where F: Fn(&T, &InvokeContext) -> Result, EbpfError>, { - let keyed_accounts = invoke_context - .get_keyed_accounts() + let instruction_context = invoke_context + .transaction_context + .get_current_instruction_context() .map_err(SyscallError::InstructionError)?; - let number_of_program_accounts = keyed_accounts.len() - - invoke_context - .get_instruction_keyed_accounts() - .map_err(SyscallError::InstructionError)? - .len(); let mut accounts = Vec::with_capacity(instruction_accounts.len().saturating_add(1)); let program_account_index = program_indices @@ -2245,10 +2259,8 @@ where let caller_account = if instruction_account.is_writable { let orig_data_len_index = instruction_account .index_in_caller - .saturating_sub(number_of_program_accounts); - if keyed_accounts[instruction_account.index_in_caller].unsigned_key() == account_key - && orig_data_len_index < orig_data_lens.len() - { + .saturating_sub(instruction_context.get_number_of_program_accounts()); + if orig_data_len_index < orig_data_lens.len() { caller_account.original_data_len = orig_data_lens[orig_data_len_index]; } else { ic_msg!( @@ -2351,7 +2363,8 @@ fn call<'a, 'b: 'a>( // Translate and verify caller's data let loader_id = invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError)?; let instruction = syscall.translate_instruction( &loader_id, @@ -2360,7 +2373,8 @@ fn call<'a, 'b: 'a>( *invoke_context, )?; let caller_program_id = invoke_context - .get_caller() + .transaction_context + .get_program_key() .map_err(SyscallError::InstructionError)?; let signers = syscall.translate_signers( &loader_id, @@ -2369,7 +2383,7 @@ fn call<'a, 'b: 'a>( signers_seeds_len, memory_mapping, )?; - let (instruction_accounts, caller_write_privileges, program_indices) = invoke_context + let (instruction_accounts, program_indices) = invoke_context .prepare_instruction(&instruction, &signers) .map_err(SyscallError::InstructionError)?; check_authorized_program(&instruction.program_id, &instruction.data, *invoke_context)?; @@ -2389,7 +2403,6 @@ fn call<'a, 'b: 'a>( .process_instruction( &instruction.data, &instruction_accounts, - Some(&caller_write_privileges), &program_indices, &mut compute_units_consumed, ) @@ -2476,7 +2489,8 @@ impl<'a, 'b> SyscallObject for SyscallSetReturnData<'a, 'b> { ); let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -2506,7 +2520,8 @@ impl<'a, 'b> SyscallObject for SyscallSetReturnData<'a, 'b> { }; let program_id = question_mark!( invoke_context - .get_caller() + .transaction_context + .get_program_key() .map_err(SyscallError::InstructionError), result ); @@ -2538,7 +2553,8 @@ impl<'a, 'b> SyscallObject for SyscallGetReturnData<'a, 'b> { ); let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -2605,7 +2621,8 @@ impl<'a, 'b> SyscallObject for SyscallLogData<'a, 'b> { ); let loader_id = question_mark!( invoke_context - .get_loader() + .transaction_context + .get_loader_key() .map_err(SyscallError::InstructionError), result ); @@ -2984,12 +3001,12 @@ mod tests { #[should_panic(expected = "UserError(SyscallError(Panic(\"Gaggablaghblagh!\", 42, 84)))")] fn test_syscall_sol_panic() { let program_id = Pubkey::new_unique(); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))], 1, ); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); - invoke_context.push(&[], &[0]).unwrap(); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); + invoke_context.push(&[], &[0], &[]).unwrap(); let mut syscall_panic = SyscallPanic { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), }; @@ -3057,12 +3074,12 @@ mod tests { #[test] fn test_syscall_sol_log() { let program_id = Pubkey::new_unique(); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))], 1, ); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); - invoke_context.push(&[], &[0]).unwrap(); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); + invoke_context.push(&[], &[0], &[]).unwrap(); let mut syscall_sol_log = SyscallLog { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), }; @@ -3157,12 +3174,12 @@ mod tests { #[test] fn test_syscall_sol_log_u64() { let program_id = Pubkey::new_unique(); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))], 1, ); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); - invoke_context.push(&[], &[0]).unwrap(); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); + invoke_context.push(&[], &[0], &[]).unwrap(); let cost = invoke_context.get_compute_budget().log_64_units; let mut syscall_sol_log_u64 = SyscallLogU64 { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3195,12 +3212,12 @@ mod tests { #[test] fn test_syscall_sol_pubkey() { let program_id = Pubkey::new_unique(); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))], 1, ); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); - invoke_context.push(&[], &[0]).unwrap(); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); + invoke_context.push(&[], &[0], &[]).unwrap(); let cost = invoke_context.get_compute_budget().log_pubkey_units; let mut syscall_sol_pubkey = SyscallLogPubkey { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3403,15 +3420,15 @@ mod tests { fn test_syscall_sha256() { let config = Config::default(); let program_id = Pubkey::new_unique(); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( vec![( program_id, AccountSharedData::new(0, 0, &bpf_loader_deprecated::id()), )], 1, ); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); - invoke_context.push(&[], &[0]).unwrap(); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); + invoke_context.push(&[], &[0], &[]).unwrap(); let bytes1 = "Gaggablaghblagh!"; let bytes2 = "flurbos"; @@ -3570,13 +3587,13 @@ mod tests { (sysvar::rent::id(), data_rent), ]; let program_id = Pubkey::new_unique(); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))], 1, ); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); invoke_context.sysvars = &sysvars; - invoke_context.push(&[], &[0]).unwrap(); + invoke_context.push(&[], &[0], &[]).unwrap(); // Test clock sysvar { @@ -3820,12 +3837,12 @@ mod tests { // These tests duplicate the direct tests in solana_program::pubkey let program_id = Pubkey::new_unique(); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))], 1, ); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); - invoke_context.push(&[], &[0]).unwrap(); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); + invoke_context.push(&[], &[0], &[]).unwrap(); let address = bpf_loader_upgradeable::id(); let exceeded_seed = &[127; MAX_SEED_LEN + 1]; @@ -3932,12 +3949,12 @@ mod tests { #[test] fn test_find_program_address() { let program_id = Pubkey::new_unique(); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( vec![(program_id, AccountSharedData::new(0, 0, &bpf_loader::id()))], 1, ); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); - invoke_context.push(&[], &[0]).unwrap(); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); + invoke_context.push(&[], &[0], &[]).unwrap(); let cost = invoke_context .get_compute_budget() .create_program_address_units; diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index 3986c5ae82..b2c591122b 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -4999,8 +4999,8 @@ mod tests { #[test] fn test_merge() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); @@ -5110,8 +5110,8 @@ mod tests { #[test] fn test_merge_self_fails() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let stake_address = Pubkey::new_unique(); let authority_pubkey = Pubkey::new_unique(); let signers = HashSet::from_iter(vec![authority_pubkey]); @@ -5156,8 +5156,8 @@ mod tests { #[test] fn test_merge_incorrect_authorized_staker() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); @@ -5226,8 +5226,8 @@ mod tests { #[test] fn test_merge_invalid_account_data() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); @@ -5277,8 +5277,8 @@ mod tests { #[test] fn test_merge_fake_stake_source() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let stake_pubkey = solana_sdk::pubkey::new_rand(); let source_stake_pubkey = solana_sdk::pubkey::new_rand(); let authorized_pubkey = solana_sdk::pubkey::new_rand(); @@ -5320,8 +5320,8 @@ mod tests { #[test] fn test_merge_active_stake() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let base_lamports = 4242424242; let stake_address = Pubkey::new_unique(); let source_address = Pubkey::new_unique(); @@ -5943,8 +5943,8 @@ mod tests { #[test] fn test_things_can_merge() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let good_stake = Stake { credits_observed: 4242, delegation: Delegation { @@ -6042,8 +6042,8 @@ mod tests { #[test] fn test_metas_can_merge_pre_v4() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); // Identical Metas can merge assert!(MergeKind::metas_can_merge( &invoke_context, @@ -6129,8 +6129,8 @@ mod tests { #[test] fn test_metas_can_merge_v4() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); // Identical Metas can merge assert!(MergeKind::metas_can_merge( &invoke_context, @@ -6276,8 +6276,8 @@ mod tests { #[test] fn test_merge_kind_get_if_mergeable() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let authority_pubkey = Pubkey::new_unique(); let initial_lamports = 4242424242; let rent = Rent::default(); @@ -6509,8 +6509,8 @@ mod tests { #[test] fn test_merge_kind_merge() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let lamports = 424242; let meta = Meta { rent_exempt_reserve: 42, @@ -6588,8 +6588,8 @@ mod tests { #[test] fn test_active_stake_merge() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let delegation_a = 4_242_424_242u64; let delegation_b = 6_200_000_000u64; let credits_a = 124_521_000u64; diff --git a/rbpf-cli/src/main.rs b/rbpf-cli/src/main.rs index 79b6fdd60d..ed9575198a 100644 --- a/rbpf-cli/src/main.rs +++ b/rbpf-cli/src/main.rs @@ -212,17 +212,21 @@ native machine code before execting it in the virtual machine.", let program_indices = [0, 1]; let preparation = prepare_mock_invoke_context(transaction_accounts, instruction_accounts, &program_indices); - let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(preparation.transaction_accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); invoke_context - .push(&preparation.instruction_accounts, &program_indices) + .push( + &preparation.instruction_accounts, + &program_indices, + &instruction_data, + ) .unwrap(); - let keyed_accounts = invoke_context.get_keyed_accounts().unwrap(); let (mut parameter_bytes, account_lengths) = serialize_parameters( - keyed_accounts[0].unsigned_key(), - keyed_accounts[1].unsigned_key(), - &keyed_accounts[2..], - &instruction_data, + invoke_context.transaction_context, + invoke_context + .transaction_context + .get_current_instruction_context() + .unwrap(), ) .unwrap(); let compute_meter = invoke_context.get_compute_meter(); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index d7ac674565..f0a9afe213 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -3561,9 +3561,9 @@ impl Bank { let mut transaction_accounts = Vec::new(); std::mem::swap(&mut loaded_transaction.accounts, &mut transaction_accounts); - let transaction_context = TransactionContext::new( + let mut transaction_context = TransactionContext::new( transaction_accounts, - compute_budget.max_invoke_depth, + compute_budget.max_invoke_depth.saturating_add(1), ); let instruction_recorder = if enable_cpi_recording { @@ -3588,7 +3588,7 @@ impl Bank { &self.builtin_programs.vec, legacy_message, &loaded_transaction.program_indices, - &transaction_context, + &mut transaction_context, self.rent_collector.rent, log_collector.clone(), executors.clone(), @@ -10401,7 +10401,7 @@ pub(crate) mod tests { _instruction_data: &[u8], invoke_context: &mut InvokeContext, ) -> std::result::Result<(), InstructionError> { - let program_id = invoke_context.get_caller()?; + let program_id = invoke_context.transaction_context.get_program_key()?; if mock_vote_program_id() != *program_id { return Err(InstructionError::IncorrectProgramId); } diff --git a/runtime/src/builtins.rs b/runtime/src/builtins.rs index 87eb176603..805a85c2ae 100644 --- a/runtime/src/builtins.rs +++ b/runtime/src/builtins.rs @@ -19,12 +19,12 @@ fn process_instruction_with_program_logging( invoke_context: &mut InvokeContext, ) -> Result<(), InstructionError> { let logger = invoke_context.get_log_collector(); - let program_id = invoke_context.get_caller()?; + let program_id = invoke_context.transaction_context.get_program_key()?; stable_log::program_invoke(&logger, program_id, invoke_context.invoke_depth()); let result = process_instruction(first_instruction_account, instruction_data, invoke_context); - let program_id = invoke_context.get_caller()?; + let program_id = invoke_context.transaction_context.get_program_key()?; match &result { Ok(()) => stable_log::program_success(&logger, program_id), Err(err) => stable_log::program_failure(&logger, program_id, err), diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 96ccebc215..8aebeb2b44 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -53,7 +53,7 @@ impl MessageProcessor { builtin_programs: &[BuiltinProgram], message: &Message, program_indices: &[Vec], - transaction_context: &TransactionContext, + transaction_context: &mut TransactionContext, rent: Rent, log_collector: Option>>, executors: Rc>, @@ -132,7 +132,6 @@ impl MessageProcessor { let result = invoke_context.process_instruction( &instruction.data, &instruction_accounts, - None, program_indices, &mut compute_units_consumed, ); @@ -242,7 +241,7 @@ mod tests { create_loadable_account_for_test("mock_system_program"), ), ]; - let transaction_context = TransactionContext::new(accounts, 1); + let mut transaction_context = TransactionContext::new(accounts, 1); let program_indices = vec![vec![2]]; let executors = Rc::new(RefCell::new(Executors::default())); let account_metas = vec![ @@ -262,7 +261,7 @@ mod tests { builtin_programs, &message, &program_indices, - &transaction_context, + &mut transaction_context, rent_collector.rent, None, executors.clone(), @@ -303,7 +302,7 @@ mod tests { builtin_programs, &message, &program_indices, - &transaction_context, + &mut transaction_context, rent_collector.rent, None, executors.clone(), @@ -336,7 +335,7 @@ mod tests { builtin_programs, &message, &program_indices, - &transaction_context, + &mut transaction_context, rent_collector.rent, None, executors, @@ -458,7 +457,7 @@ mod tests { create_loadable_account_for_test("mock_system_program"), ), ]; - let transaction_context = TransactionContext::new(accounts, 1); + let mut transaction_context = TransactionContext::new(accounts, 1); let program_indices = vec![vec![2]]; let executors = Rc::new(RefCell::new(Executors::default())); let account_metas = vec![ @@ -480,7 +479,7 @@ mod tests { builtin_programs, &message, &program_indices, - &transaction_context, + &mut transaction_context, rent_collector.rent, None, executors.clone(), @@ -514,7 +513,7 @@ mod tests { builtin_programs, &message, &program_indices, - &transaction_context, + &mut transaction_context, rent_collector.rent, None, executors.clone(), @@ -545,7 +544,7 @@ mod tests { builtin_programs, &message, &program_indices, - &transaction_context, + &mut transaction_context, rent_collector.rent, None, executors, @@ -602,7 +601,7 @@ mod tests { (secp256k1_program::id(), secp256k1_account), (mock_program_id, mock_program_account), ]; - let transaction_context = TransactionContext::new(accounts, 1); + let mut transaction_context = TransactionContext::new(accounts, 1); let message = Message::new( &[ @@ -618,7 +617,7 @@ mod tests { builtin_programs, &message, &[vec![0], vec![1]], - &transaction_context, + &mut transaction_context, RentCollector::default().rent, None, Rc::new(RefCell::new(Executors::default())), diff --git a/runtime/src/nonce_keyed_account.rs b/runtime/src/nonce_keyed_account.rs index d700fdff43..591db87635 100644 --- a/runtime/src/nonce_keyed_account.rs +++ b/runtime/src/nonce_keyed_account.rs @@ -334,8 +334,8 @@ mod test { where F: FnMut(&mut InvokeContext, &KeyedAccount), { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let pubkey = Pubkey::new_unique(); let account = create_account(lamports); let keyed_account = KeyedAccount::new(&pubkey, signer, &account); diff --git a/runtime/src/system_instruction_processor.rs b/runtime/src/system_instruction_processor.rs index b9639b35fa..ab0c51e48a 100644 --- a/runtime/src/system_instruction_processor.rs +++ b/runtime/src/system_instruction_processor.rs @@ -675,8 +675,8 @@ mod tests { #[test] fn test_address_create_with_seed_mismatch() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let from = Pubkey::new_unique(); let seed = "dull boy"; let to = Pubkey::new_unique(); @@ -690,8 +690,8 @@ mod tests { #[test] fn test_create_account_with_seed_missing_sig() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); let seed = "dull boy"; @@ -721,8 +721,8 @@ mod tests { #[test] fn test_create_with_zero_lamports() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); // create account with zero lamports transferred let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); @@ -756,8 +756,8 @@ mod tests { #[test] fn test_create_negative_lamports() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); // Attempt to create account with more lamports than remaining in from_account let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); @@ -781,8 +781,8 @@ mod tests { #[test] fn test_request_more_than_allowed_data_length() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let from_account = RefCell::new(AccountSharedData::new(100, 0, &system_program::id())); let from = Pubkey::new_unique(); let to_account = RefCell::new(AccountSharedData::new(0, 0, &system_program::id())); @@ -829,8 +829,8 @@ mod tests { #[test] fn test_create_already_in_use() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); // Attempt to create system account in account already owned by another program let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); @@ -898,8 +898,8 @@ mod tests { #[test] fn test_create_unsigned() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); // Attempt to create an account without signing the transfer let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); @@ -954,8 +954,8 @@ mod tests { #[test] fn test_create_sysvar_invalid_id_with_feature() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); // Attempt to create system account in account already owned by another program let from = Pubkey::new_unique(); let from_account = RefCell::new(AccountSharedData::new(100, 0, &system_program::id())); @@ -989,8 +989,8 @@ mod tests { feature_set .inactive .insert(feature_set::rent_for_sysvars::id()); - let transaction_context = TransactionContext::new(Vec::new(), 1); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); invoke_context.feature_set = Arc::new(feature_set); // Attempt to create system account in account already owned by another program let from = Pubkey::new_unique(); @@ -1017,8 +1017,8 @@ mod tests { #[test] fn test_create_data_populated() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); // Attempt to create system account in account with populated data let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); @@ -1051,8 +1051,8 @@ mod tests { #[test] fn test_create_from_account_is_nonce_fail() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let nonce = Pubkey::new_unique(); let nonce_account = RefCell::new( AccountSharedData::new_data( @@ -1090,8 +1090,8 @@ mod tests { #[test] fn test_assign() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let new_owner = Pubkey::new(&[9; 32]); let pubkey = Pubkey::new_unique(); let mut account = AccountSharedData::new(100, 0, &system_program::id()); @@ -1133,8 +1133,8 @@ mod tests { #[test] fn test_assign_to_sysvar_with_feature() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let new_owner = sysvar::id(); let from = Pubkey::new_unique(); let mut from_account = AccountSharedData::new(100, 0, &system_program::id()); @@ -1160,8 +1160,8 @@ mod tests { feature_set .inactive .insert(feature_set::rent_for_sysvars::id()); - let transaction_context = TransactionContext::new(Vec::new(), 1); - let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); invoke_context.feature_set = Arc::new(feature_set); let new_owner = sysvar::id(); let from = Pubkey::new_unique(); @@ -1212,8 +1212,8 @@ mod tests { #[test] fn test_transfer_lamports() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let from = Pubkey::new_unique(); let from_account = RefCell::new(AccountSharedData::new(100, 0, &Pubkey::new(&[2; 32]))); // account owner should not matter let to = Pubkey::new(&[3; 32]); @@ -1251,8 +1251,8 @@ mod tests { #[test] fn test_transfer_with_seed() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let base = Pubkey::new_unique(); let base_account = RefCell::new(AccountSharedData::new(100, 0, &Pubkey::new(&[2; 32]))); // account owner should not matter let from_base_keyed_account = KeyedAccount::new(&base, true, &base_account); @@ -1312,8 +1312,8 @@ mod tests { #[test] fn test_transfer_lamports_from_nonce_account_fail() { - let transaction_context = TransactionContext::new(Vec::new(), 1); - let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); + let mut transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]); let from = Pubkey::new_unique(); let from_account = RefCell::new( AccountSharedData::new_data( diff --git a/sdk/src/transaction_context.rs b/sdk/src/transaction_context.rs index 1e0badc2fe..4dbbb1e837 100644 --- a/sdk/src/transaction_context.rs +++ b/sdk/src/transaction_context.rs @@ -6,7 +6,10 @@ use crate::{ lamports::LamportsError, pubkey::Pubkey, }; -use std::cell::{RefCell, RefMut}; +use std::{ + cell::{RefCell, RefMut}, + pin::Pin, +}; pub type TransactionAccount = (Pubkey, AccountSharedData); @@ -23,8 +26,8 @@ pub struct InstructionAccount { /// This context is valid for the entire duration of a transaction being processed. #[derive(Debug)] pub struct TransactionContext { - account_keys: Vec, - accounts: Vec>, + account_keys: Pin>, + accounts: Pin]>>, instruction_context_capacity: usize, instruction_context_stack: Vec, return_data: (Pubkey, Vec), @@ -36,13 +39,14 @@ impl TransactionContext { transaction_accounts: Vec, instruction_context_capacity: usize, ) -> Self { - let (account_keys, accounts) = transaction_accounts - .into_iter() - .map(|(key, account)| (key, RefCell::new(account))) - .unzip(); + let (account_keys, accounts): (Vec, Vec>) = + transaction_accounts + .into_iter() + .map(|(key, account)| (key, RefCell::new(account))) + .unzip(); Self { - account_keys, - accounts, + account_keys: Pin::new(account_keys.into_boxed_slice()), + accounts: Pin::new(accounts.into_boxed_slice()), instruction_context_capacity, instruction_context_stack: Vec::with_capacity(instruction_context_capacity), return_data: (Pubkey::default(), Vec::new()), @@ -51,10 +55,10 @@ impl TransactionContext { /// Used by the bank in the runtime to write back the processed accounts pub fn deconstruct(self) -> Vec { - self.account_keys + Vec::from(Pin::into_inner(self.account_keys)) .into_iter() .zip( - self.accounts + Vec::from(Pin::into_inner(self.accounts)) .into_iter() .map(|account| account.into_inner()), ) @@ -66,8 +70,7 @@ impl TransactionContext { if !self.instruction_context_stack.is_empty() { return Err(InstructionError::CallDepth); } - Ok(self - .accounts + Ok(Vec::from(Pin::into_inner(self.accounts)) .into_iter() .map(|account| account.into_inner()) .collect()) @@ -243,21 +246,29 @@ impl InstructionContext { .map(|index| index.saturating_add(self.program_accounts.len())) } + /// Translates the given instruction wide index into a transaction wide index + pub fn get_index_in_transaction( + &self, + index_in_instruction: usize, + ) -> Result { + if index_in_instruction < self.program_accounts.len() { + Ok(self.program_accounts[index_in_instruction]) + } else if index_in_instruction < self.get_number_of_accounts() { + Ok(self.instruction_accounts + [index_in_instruction.saturating_sub(self.program_accounts.len())] + .index_in_transaction) + } else { + Err(InstructionError::NotEnoughAccountKeys) + } + } + /// Tries to borrow an account from this Instruction pub fn try_borrow_account<'a, 'b: 'a>( &'a self, transaction_context: &'b TransactionContext, index_in_instruction: usize, ) -> Result, InstructionError> { - let index_in_transaction = if index_in_instruction < self.program_accounts.len() { - self.program_accounts[index_in_instruction] - } else if index_in_instruction < self.get_number_of_accounts() { - self.instruction_accounts - [index_in_instruction.saturating_sub(self.program_accounts.len())] - .index_in_transaction - } else { - return Err(InstructionError::NotEnoughAccountKeys); - }; + let index_in_transaction = self.get_index_in_transaction(index_in_instruction)?; if index_in_transaction >= transaction_context.accounts.len() { return Err(InstructionError::MissingAccount); } @@ -331,12 +342,13 @@ impl<'a> BorrowedAccount<'a> { } /// Assignes the owner of this account (transaction wide) - pub fn set_owner(&mut self, pubkey: Pubkey) -> Result<(), InstructionError> { - if !self.is_writable() { - return Err(InstructionError::Immutable); + pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> { + self.account.copy_into_owner_from_slice(pubkey); + if self.is_writable() { + Ok(()) + } else { + Err(InstructionError::Immutable) } - self.account.set_owner(pubkey); - Ok(()) } /// Returns the number of lamports of this account (transaction wide) @@ -346,11 +358,12 @@ impl<'a> BorrowedAccount<'a> { /// Overwrites the number of lamports of this account (transaction wide) pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> { - if !self.is_writable() { - return Err(InstructionError::Immutable); - } self.account.set_lamports(lamports); - Ok(()) + if self.is_writable() { + Ok(()) + } else { + Err(InstructionError::Immutable) + } } /// Adds lamports to this account (transaction wide) @@ -376,26 +389,23 @@ impl<'a> BorrowedAccount<'a> { self.account.data() } - /// Returns a writable slice of the account data (transaction wide) - pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> { - if !self.is_writable() { - return Err(InstructionError::Immutable); + /// Overwrites the account data and size (transaction wide) + pub fn set_data(&mut self, data: &[u8]) -> Result<(), InstructionError> { + if data.len() == self.account.data().len() { + self.account.data_as_mut_slice().copy_from_slice(data); + } else { + self.account.set_data_from_slice(data); + } + if self.is_writable() { + Ok(()) + } else { + Err(InstructionError::Immutable) } - Ok(self.account.data_as_mut_slice()) } - /// Guarded alternative to `get_data_mut()?.copy_from_slice()` which checks if the account size matches - pub fn copy_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> { - if !self.is_writable() { - return Err(InstructionError::Immutable); - } - let account_data = self.account.data_as_mut_slice(); - if data.len() != account_data.len() { - return Err(InstructionError::AccountDataSizeChanged); - } - account_data.copy_from_slice(data); - Ok(()) - } + /*pub fn realloc(&self, new_len: usize, zero_init: bool) { + // TODO + }*/ /// Deserializes the account data into a state pub fn get_state(&self) -> Result { @@ -406,22 +416,20 @@ impl<'a> BorrowedAccount<'a> { /// Serializes a state into the account data pub fn set_state(&mut self, state: &T) -> Result<(), InstructionError> { - if !self.is_writable() { - return Err(InstructionError::Immutable); - } let data = self.account.data_as_mut_slice(); let serialized_size = bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?; if serialized_size > data.len() as u64 { return Err(InstructionError::AccountDataTooSmall); } - bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError) + bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?; + if self.is_writable() { + Ok(()) + } else { + Err(InstructionError::Immutable) + } } - /*pub fn realloc(&self, new_len: usize, zero_init: bool) { - // TODO - }*/ - /// Returns whether this account is executable (transaction wide) pub fn is_executable(&self) -> bool { self.account.executable() @@ -429,11 +437,17 @@ impl<'a> BorrowedAccount<'a> { /// Configures whether this account is executable (transaction wide) pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> { - if !self.is_writable() { - return Err(InstructionError::Immutable); - } self.account.set_executable(is_executable); - Ok(()) + if self.is_writable() { + Ok(()) + } else { + Err(InstructionError::Immutable) + } + } + + /// Returns the rent epoch of this account (transaction wide) + pub fn get_rent_epoch(&self) -> u64 { + self.account.rent_epoch() } /// Returns whether this account is a signer (instruction wide)