diff --git a/cli/src/program.rs b/cli/src/program.rs index 6fce75754..176018559 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -42,6 +42,7 @@ use { system_instruction::{self, SystemError}, system_program, transaction::{Transaction, TransactionError}, + transaction_context::TransactionContext, }, std::{ fs::File, @@ -1990,7 +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 dc40eb325..afb677ccd 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -20,20 +20,11 @@ use { pubkey::Pubkey, rent::Rent, sysvar::Sysvar, + transaction_context::{InstructionAccount, TransactionAccount, TransactionContext}, }, std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc, sync::Arc}, }; -pub type TransactionAccountRefCell = (Pubkey, RefCell); -pub type TransactionAccountRefCells = Vec; - -#[derive(Clone, Debug)] -pub struct InstructionAccount { - pub index: usize, - pub is_signer: bool, - pub is_writable: bool, -} - pub type ProcessInstructionWithContext = fn(usize, &[u8], &mut InvokeContext) -> Result<(), InstructionError>; @@ -144,10 +135,11 @@ impl<'a> StackFrame<'a> { } pub struct InvokeContext<'a> { + pub transaction_context: &'a TransactionContext, + pub return_data: (Pubkey, Vec), invoke_stack: Vec>, rent: Rent, pre_accounts: Vec, - accounts: &'a [TransactionAccountRefCell], builtin_programs: &'a [BuiltinProgram], pub sysvars: &'a [(Pubkey, Vec)], log_collector: Option>>, @@ -160,14 +152,13 @@ pub struct InvokeContext<'a> { pub timings: ExecuteDetailsTimings, pub blockhash: Hash, pub lamports_per_signature: u64, - pub return_data: (Pubkey, Vec), } impl<'a> InvokeContext<'a> { #[allow(clippy::too_many_arguments)] pub fn new( + transaction_context: &'a TransactionContext, rent: Rent, - accounts: &'a [TransactionAccountRefCell], builtin_programs: &'a [BuiltinProgram], sysvars: &'a [(Pubkey, Vec)], log_collector: Option>>, @@ -179,10 +170,11 @@ impl<'a> InvokeContext<'a> { lamports_per_signature: u64, ) -> Self { Self { + transaction_context, + return_data: (Pubkey::default(), Vec::new()), invoke_stack: Vec::with_capacity(compute_budget.max_invoke_depth), rent, pre_accounts: Vec::new(), - accounts, builtin_programs, sysvars, log_collector, @@ -195,17 +187,16 @@ impl<'a> InvokeContext<'a> { timings: ExecuteDetailsTimings::default(), blockhash, lamports_per_signature, - return_data: (Pubkey::default(), Vec::new()), } } pub fn new_mock( - accounts: &'a [TransactionAccountRefCell], + transaction_context: &'a TransactionContext, builtin_programs: &'a [BuiltinProgram], ) -> Self { Self::new( + transaction_context, Rent::default(), - accounts, builtin_programs, &[], Some(LogCollector::new_ref()), @@ -228,9 +219,10 @@ impl<'a> InvokeContext<'a> { return Err(InstructionError::CallDepth); } - let program_id = program_indices - .last() - .map(|index_of_program_id| &self.accounts[*index_of_program_id].0); + let program_id = program_indices.last().map(|account_index| { + self.transaction_context + .get_key_of_account_at_index(*account_index) + }); if program_id.is_none() && self .feature_set @@ -262,10 +254,17 @@ impl<'a> InvokeContext<'a> { self.pre_accounts = Vec::with_capacity(instruction_accounts.len()); let mut work = |_index_in_instruction: usize, entry: &InstructionAccount| { - if entry.index < self.accounts.len() { - let account = self.accounts[entry.index].1.borrow().clone(); - self.pre_accounts - .push(PreAccount::new(&self.accounts[entry.index].0, account)); + if entry.index < self.transaction_context.get_number_of_accounts() { + let account = self + .transaction_context + .get_account_at_index(entry.index) + .borrow() + .clone(); + self.pre_accounts.push(PreAccount::new( + self.transaction_context + .get_key_of_account_at_index(entry.index), + account, + )); return Ok(()); } Err(InstructionError::MissingAccount) @@ -294,16 +293,20 @@ impl<'a> InvokeContext<'a> { ( false, false, - &self.accounts[*account_index].0, - &self.accounts[*account_index].1 as &RefCell, + self.transaction_context + .get_key_of_account_at_index(*account_index), + self.transaction_context + .get_account_at_index(*account_index), ) }) .chain(instruction_accounts.iter().map(|instruction_account| { ( instruction_account.is_signer, instruction_account.is_writable, - &self.accounts[instruction_account.index].0, - &self.accounts[instruction_account.index].1 as &RefCell, + self.transaction_context + .get_key_of_account_at_index(instruction_account.index), + self.transaction_context + .get_account_at_index(instruction_account.index), ) })) .collect::>(); @@ -340,8 +343,8 @@ impl<'a> InvokeContext<'a> { // Verify all executable accounts have zero outstanding refs for account_index in program_indices.iter() { - self.accounts[*account_index] - .1 + self.transaction_context + .get_account_at_index(*account_index) .try_borrow_mut() .map_err(|_| InstructionError::AccountBorrowOutstanding)?; } @@ -352,14 +355,18 @@ impl<'a> InvokeContext<'a> { let mut work = |_index_in_instruction: usize, instruction_account: &InstructionAccount| { { // Verify account has no outstanding references - let _ = self.accounts[instruction_account.index] - .1 + let _ = self + .transaction_context + .get_account_at_index(instruction_account.index) .try_borrow_mut() .map_err(|_| InstructionError::AccountBorrowOutstanding)?; } let pre_account = &self.pre_accounts[pre_account_index]; pre_account_index = pre_account_index.saturating_add(1); - let account = self.accounts[instruction_account.index].1.borrow(); + let account = self + .transaction_context + .get_account_at_index(instruction_account.index) + .borrow(); pre_account .verify( program_id, @@ -410,15 +417,17 @@ impl<'a> InvokeContext<'a> { .ok_or(InstructionError::CallDepth)?; let rent = &self.rent; let log_collector = &self.log_collector; - let accounts = &self.accounts; + let transaction_context = self.transaction_context; let pre_accounts = &mut self.pre_accounts; let timings = &mut self.timings; // 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| { - if instruction_account.index < accounts.len() { - let (key, account) = &accounts[instruction_account.index]; + if instruction_account.index < transaction_context.get_number_of_accounts() { + let key = + transaction_context.get_key_of_account_at_index(instruction_account.index); + let account = transaction_context.get_account_at_index(instruction_account.index); let is_writable = if let Some(caller_write_privileges) = caller_write_privileges { caller_write_privileges[index_in_instruction] } else { @@ -487,8 +496,9 @@ impl<'a> InvokeContext<'a> { self.prepare_instruction(&instruction, signers)?; let mut prev_account_sizes = Vec::with_capacity(instruction_accounts.len()); for instruction_account in instruction_accounts.iter() { - let account_length = self.accounts[instruction_account.index] - .1 + let account_length = self + .transaction_context + .get_account_at_index(instruction_account.index) .borrow() .data() .len(); @@ -506,7 +516,13 @@ impl<'a> InvokeContext<'a> { let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id()); for (account_index, prev_size) in prev_account_sizes.into_iter() { if !do_support_realloc - && prev_size != self.accounts[account_index].1.borrow().data().len() + && prev_size + != self + .transaction_context + .get_account_at_index(account_index) + .borrow() + .data() + .len() && prev_size != 0 { // Only support for `CreateAccount` at this time. @@ -536,9 +552,8 @@ impl<'a> InvokeContext<'a> { let mut duplicate_indicies = Vec::with_capacity(instruction.accounts.len()); for account_meta in instruction.accounts.iter() { let account_index = self - .accounts - .iter() - .position(|(key, _account)| key == &account_meta.pubkey) + .transaction_context + .find_index_of_account(&account_meta.pubkey) .ok_or_else(|| { ic_msg!( self, @@ -617,15 +632,17 @@ impl<'a> InvokeContext<'a> { .iter() .find(|keyed_account| &callee_program_id == keyed_account.unsigned_key()) .and_then(|_keyed_account| { - self.accounts - .iter() - .rposition(|(key, _account)| key == &callee_program_id) + self.transaction_context + .find_index_of_program_account(&callee_program_id) }) .ok_or_else(|| { ic_msg!(self, "Unknown program {}", callee_program_id); InstructionError::MissingAccount })?; - let program_account = self.accounts[program_account_index].1.borrow(); + let program_account = self + .transaction_context + .get_account_at_index(program_account_index) + .borrow(); if !program_account.executable() { ic_msg!(self, "Account {} is not executable", callee_program_id); return Err(InstructionError::AccountNotExecutable); @@ -637,9 +654,8 @@ impl<'a> InvokeContext<'a> { } = program_account.state()? { if let Some(programdata_account_index) = self - .accounts - .iter() - .rposition(|(key, _account)| key == &programdata_address) + .transaction_context + .find_index_of_program_account(&programdata_address) { program_indices.push(programdata_account_index); } else { @@ -678,7 +694,7 @@ impl<'a> InvokeContext<'a> { ) -> Result { let program_id = program_indices .last() - .map(|index| self.accounts[*index].0) + .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(); @@ -694,9 +710,8 @@ impl<'a> InvokeContext<'a> { if let Some(instruction_recorder) = &self.instruction_recorder { let compiled_instruction = CompiledInstruction { program_id_index: self - .accounts - .iter() - .position(|(key, _account)| *key == program_id) + .transaction_context + .find_index_of_account(&program_id) .unwrap_or(0) as u8, data: instruction_data.to_vec(), accounts: instruction_accounts @@ -852,16 +867,6 @@ impl<'a> InvokeContext<'a> { self.executors.borrow().get(pubkey) } - /// Returns an account by its account_index - pub fn get_account_key_at_index(&self, account_index: usize) -> &Pubkey { - &self.accounts[account_index].0 - } - - /// Returns an account by its account_index - pub fn get_account_at_index(&self, account_index: usize) -> &RefCell { - &self.accounts[account_index].1 - } - /// Get this invocation's compute budget pub fn get_compute_budget(&self) -> &ComputeBudget { &self.current_compute_budget @@ -886,18 +891,14 @@ impl<'a> InvokeContext<'a> { } pub struct MockInvokeContextPreparation { - pub transaction_accounts: TransactionAccountRefCells, + pub transaction_accounts: Vec, pub instruction_accounts: Vec, } pub fn prepare_mock_invoke_context( - transaction_accounts: Vec<(Pubkey, AccountSharedData)>, + transaction_accounts: Vec, instruction_accounts: Vec, ) -> MockInvokeContextPreparation { - let transaction_accounts: TransactionAccountRefCells = transaction_accounts - .into_iter() - .map(|(pubkey, account)| (pubkey, RefCell::new(account))) - .collect(); let instruction_accounts = instruction_accounts .iter() .map(|account_meta| InstructionAccount { @@ -941,7 +942,11 @@ pub fn with_mock_invoke_context R>( is_writable: false, }]; let preparation = prepare_mock_invoke_context(transaction_accounts, instruction_accounts); - let mut invoke_context = InvokeContext::new_mock(&preparation.transaction_accounts, &[]); + let transaction_context = TransactionContext::new( + preparation.transaction_accounts, + ComputeBudget::default().max_invoke_depth, + ); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); invoke_context .push(&preparation.instruction_accounts, &program_indices) .unwrap(); @@ -952,41 +957,38 @@ pub fn mock_process_instruction_with_sysvars( loader_id: &Pubkey, mut program_indices: Vec, instruction_data: &[u8], - transaction_accounts: Vec<(Pubkey, AccountSharedData)>, + transaction_accounts: Vec, instruction_accounts: Vec, expected_result: Result<(), InstructionError>, sysvars: &[(Pubkey, Vec)], process_instruction: ProcessInstructionWithContext, ) -> Vec { let mut preparation = prepare_mock_invoke_context(transaction_accounts, instruction_accounts); - let processor_account = RefCell::new(AccountSharedData::new( - 0, - 0, - &solana_sdk::native_loader::id(), - )); + let processor_account = AccountSharedData::new(0, 0, &solana_sdk::native_loader::id()); program_indices.insert(0, preparation.transaction_accounts.len()); preparation .transaction_accounts .push((*loader_id, processor_account)); - let mut invoke_context = InvokeContext::new_mock(&preparation.transaction_accounts, &[]); + let transaction_context = TransactionContext::new( + preparation.transaction_accounts, + ComputeBudget::default().max_invoke_depth, + ); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); invoke_context.sysvars = sysvars; let result = invoke_context .push(&preparation.instruction_accounts, &program_indices) .and_then(|_| process_instruction(1, instruction_data, &mut invoke_context)); - preparation.transaction_accounts.pop(); assert_eq!(result, expected_result); - preparation - .transaction_accounts - .into_iter() - .map(|(_key, account)| account.into_inner()) - .collect() + let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap(); + transaction_accounts.pop(); + transaction_accounts } pub fn mock_process_instruction( loader_id: &Pubkey, program_indices: Vec, instruction_data: &[u8], - transaction_accounts: Vec<(Pubkey, AccountSharedData)>, + transaction_accounts: Vec, instruction_accounts: Vec, expected_result: Result<(), InstructionError>, process_instruction: ProcessInstructionWithContext, @@ -1166,11 +1168,7 @@ mod tests { invoke_stack.push(solana_sdk::pubkey::new_rand()); accounts.push(( solana_sdk::pubkey::new_rand(), - RefCell::new(AccountSharedData::new( - index as u64, - 1, - &invoke_stack[index], - )), + AccountSharedData::new(index as u64, 1, &invoke_stack[index]), )); instruction_accounts.push(InstructionAccount { index, @@ -1181,11 +1179,7 @@ mod tests { for (index, program_id) in invoke_stack.iter().enumerate() { accounts.push(( *program_id, - RefCell::new(AccountSharedData::new( - 1, - 1, - &solana_sdk::pubkey::Pubkey::default(), - )), + AccountSharedData::new(1, 1, &solana_sdk::pubkey::Pubkey::default()), )); instruction_accounts.push(InstructionAccount { index, @@ -1193,7 +1187,8 @@ mod tests { is_writable: false, }); } - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); + let transaction_context = TransactionContext::new(accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); // Check call depth increases and has a limit let mut depth_reached = 0; @@ -1225,8 +1220,10 @@ mod tests { ]; // modify account owned by the program - accounts[owned_index].1.borrow_mut().data_as_mut_slice()[0] = - (MAX_DEPTH + owned_index) as u8; + 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) .unwrap(); @@ -1236,15 +1233,23 @@ mod tests { ); // modify account not owned by the program - let data = accounts[not_owned_index].1.borrow_mut().data()[0]; - accounts[not_owned_index].1.borrow_mut().data_as_mut_slice()[0] = - (MAX_DEPTH + not_owned_index) as u8; + let data = transaction_context + .get_account_at_index(not_owned_index) + .borrow_mut() + .data()[0]; + 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), Err(InstructionError::ExternalAccountDataModified) ); assert_eq!(invoke_context.pre_accounts[not_owned_index].data()[0], data); - accounts[not_owned_index].1.borrow_mut().data_as_mut_slice()[0] = data; + transaction_context + .get_account_at_index(not_owned_index) + .borrow_mut() + .data_as_mut_slice()[0] = data; invoke_context.pop(); } @@ -1252,13 +1257,11 @@ mod tests { #[test] fn test_invoke_context_verify() { - let accounts = vec![( - solana_sdk::pubkey::new_rand(), - RefCell::new(AccountSharedData::default()), - )]; + let accounts = vec![(solana_sdk::pubkey::new_rand(), AccountSharedData::default())]; let instruction_accounts = vec![]; let program_indices = vec![0]; - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); + let transaction_context = TransactionContext::new(accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); invoke_context .push(&instruction_accounts, &program_indices) .unwrap(); @@ -1266,7 +1269,7 @@ mod tests { .verify(&instruction_accounts, &program_indices) .is_ok()); - let mut _borrowed = accounts[0].1.borrow(); + let mut _borrowed = transaction_context.get_account_at_index(0).borrow(); assert_eq!( invoke_context.verify(&instruction_accounts, &program_indices), Err(InstructionError::AccountBorrowOutstanding) @@ -1277,6 +1280,10 @@ mod tests { fn test_process_cross_program() { let caller_program_id = solana_sdk::pubkey::new_rand(); let callee_program_id = solana_sdk::pubkey::new_rand(); + let builtin_programs = &[BuiltinProgram { + program_id: callee_program_id, + process_instruction: mock_process_instruction, + }]; let owned_account = AccountSharedData::new(42, 1, &callee_program_id); let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand()); @@ -1286,17 +1293,11 @@ mod tests { program_account.set_executable(true); let accounts = vec![ - (solana_sdk::pubkey::new_rand(), RefCell::new(owned_account)), - ( - solana_sdk::pubkey::new_rand(), - RefCell::new(not_owned_account), - ), - ( - solana_sdk::pubkey::new_rand(), - RefCell::new(readonly_account), - ), - (caller_program_id, RefCell::new(loader_account)), - (callee_program_id, RefCell::new(program_account)), + (solana_sdk::pubkey::new_rand(), owned_account), + (solana_sdk::pubkey::new_rand(), not_owned_account), + (solana_sdk::pubkey::new_rand(), readonly_account), + (caller_program_id, loader_account), + (callee_program_id, program_account), ]; let program_indices = [3, 4]; @@ -1319,17 +1320,17 @@ mod tests { &MockInstruction::NoopSuccess, metas.clone(), ); - let builtin_programs = &[BuiltinProgram { - program_id: callee_program_id, - process_instruction: mock_process_instruction, - }]; - let mut invoke_context = InvokeContext::new_mock(&accounts, builtin_programs); + let transaction_context = TransactionContext::new(accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, builtin_programs); invoke_context .push(&instruction_accounts, &program_indices[..1]) .unwrap(); // not owned account modified by the caller (before the invoke) - accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 1; + transaction_context + .get_account_at_index(1) + .borrow_mut() + .data_as_mut_slice()[0] = 1; assert_eq!( invoke_context.process_instruction( &instruction.data, @@ -1339,10 +1340,16 @@ mod tests { ), Err(InstructionError::ExternalAccountDataModified) ); - accounts[0].1.borrow_mut().data_as_mut_slice()[0] = 0; + transaction_context + .get_account_at_index(1) + .borrow_mut() + .data_as_mut_slice()[0] = 0; // readonly account modified by the invoker - accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 1; + transaction_context + .get_account_at_index(2) + .borrow_mut() + .data_as_mut_slice()[0] = 1; assert_eq!( invoke_context.process_instruction( &instruction.data, @@ -1352,7 +1359,10 @@ mod tests { ), Err(InstructionError::ReadonlyDataModified) ); - accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 0; + transaction_context + .get_account_at_index(2) + .borrow_mut() + .data_as_mut_slice()[0] = 0; invoke_context.pop(); @@ -1401,16 +1411,10 @@ mod tests { let mut program_account = AccountSharedData::new(1, 0, &native_loader::id()); program_account.set_executable(true); let accounts = vec![ - (solana_sdk::pubkey::new_rand(), RefCell::new(owned_account)), - ( - solana_sdk::pubkey::new_rand(), - RefCell::new(not_owned_account), - ), - ( - solana_sdk::pubkey::new_rand(), - RefCell::new(readonly_account), - ), - (callee_program_id, RefCell::new(program_account)), + (solana_sdk::pubkey::new_rand(), owned_account), + (solana_sdk::pubkey::new_rand(), not_owned_account), + (solana_sdk::pubkey::new_rand(), readonly_account), + (callee_program_id, program_account), ]; let program_indices = [3]; @@ -1435,26 +1439,39 @@ mod tests { metas.clone(), ); - let mut invoke_context = InvokeContext::new_mock(&accounts, builtin_programs); + let transaction_context = TransactionContext::new(accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, builtin_programs); invoke_context .push(&instruction_accounts, &program_indices) .unwrap(); // not owned account modified by the invoker - accounts[1].1.borrow_mut().data_as_mut_slice()[0] = 1; + transaction_context + .get_account_at_index(1) + .borrow_mut() + .data_as_mut_slice()[0] = 1; assert_eq!( invoke_context.native_invoke(callee_instruction.clone(), &[]), Err(InstructionError::ExternalAccountDataModified) ); - accounts[1].1.borrow_mut().data_as_mut_slice()[0] = 0; + transaction_context + .get_account_at_index(1) + .borrow_mut() + .data_as_mut_slice()[0] = 0; // readonly account modified by the invoker - accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 1; + transaction_context + .get_account_at_index(2) + .borrow_mut() + .data_as_mut_slice()[0] = 1; assert_eq!( invoke_context.native_invoke(callee_instruction, &[]), Err(InstructionError::ReadonlyDataModified) ); - accounts[2].1.borrow_mut().data_as_mut_slice()[0] = 0; + transaction_context + .get_account_at_index(2) + .borrow_mut() + .data_as_mut_slice()[0] = 0; invoke_context.pop(); @@ -1492,20 +1509,15 @@ mod tests { #[test] fn test_invoke_context_compute_budget() { let accounts = vec![ - ( - solana_sdk::pubkey::new_rand(), - RefCell::new(AccountSharedData::default()), - ), - ( - crate::neon_evm_program::id(), - RefCell::new(AccountSharedData::default()), - ), + (solana_sdk::pubkey::new_rand(), AccountSharedData::default()), + (crate::neon_evm_program::id(), AccountSharedData::default()), ]; let mut feature_set = FeatureSet::all_enabled(); feature_set.deactivate(&tx_wide_compute_cap::id()); feature_set.deactivate(&requestable_heap_size::id()); - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); + let transaction_context = TransactionContext::new(accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); invoke_context.feature_set = Arc::new(feature_set); invoke_context.push(&[], &[0]).unwrap(); diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index 4d5327d3d..23d640d18 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -252,7 +252,9 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { // Convert AccountInfos into Accounts let mut accounts = Vec::with_capacity(instruction_accounts.len()); for instruction_account in instruction_accounts.iter() { - let account_key = invoke_context.get_account_key_at_index(instruction_account.index); + let account_key = invoke_context + .transaction_context + .get_key_of_account_at_index(instruction_account.index); let account_info = account_infos .iter() .find(|account_info| account_info.unsigned_key() == account_key) @@ -260,6 +262,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { .unwrap(); { let mut account = invoke_context + .transaction_context .get_account_at_index(instruction_account.index) .borrow_mut(); account.copy_into_owner_from_slice(account_info.owner.as_ref()); @@ -284,7 +287,9 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { // Copy writeable account modifications back into the caller's AccountInfos for (account_index, account_info) in accounts.into_iter() { - let account = invoke_context.get_account_at_index(account_index); + let account = invoke_context + .transaction_context + .get_account_at_index(account_index); let account_borrow = account.borrow(); **account_info.try_borrow_mut_lamports().unwrap() = account_borrow.lamports(); let mut data = account_info.try_borrow_mut_data()?; diff --git a/programs/bpf_loader/src/serialization.rs b/programs/bpf_loader/src/serialization.rs index 1e3786e24..994e060e4 100644 --- a/programs/bpf_loader/src/serialization.rs +++ b/programs/bpf_loader/src/serialization.rs @@ -323,6 +323,7 @@ mod tests { bpf_loader, entrypoint::deserialize, instruction::AccountMeta, + transaction_context::TransactionContext, }, std::{ cell::RefCell, @@ -452,7 +453,8 @@ mod tests { let program_indices = [0]; let preparation = prepare_mock_invoke_context(transaction_accounts.clone(), instruction_accounts); - let mut invoke_context = InvokeContext::new_mock(&preparation.transaction_accounts, &[]); + let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); invoke_context .push(&preparation.instruction_accounts, &program_indices) .unwrap(); diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 2d9406fac..15de33ac6 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -5,7 +5,7 @@ use { alloc::Alloc, solana_program_runtime::{ ic_logger_msg, ic_msg, - invoke_context::{ComputeMeter, InstructionAccount, InvokeContext}, + invoke_context::{ComputeMeter, InvokeContext}, stable_log, }, solana_rbpf::{ @@ -41,6 +41,7 @@ use { Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH, }, sysvar::{self, Sysvar, SysvarId}, + transaction_context::InstructionAccount, }, std::{ alloc::Layout, @@ -2214,8 +2215,12 @@ where accounts.push((*program_account_index, None)); for instruction_account in instruction_accounts.iter() { - let account = invoke_context.get_account_at_index(instruction_account.index); - let account_key = invoke_context.get_account_key_at_index(instruction_account.index); + let account = invoke_context + .transaction_context + .get_account_at_index(instruction_account.index); + let account_key = invoke_context + .transaction_context + .get_key_of_account_at_index(instruction_account.index); if account.borrow().executable() { // Use the known account accounts.push((instruction_account.index, None)); @@ -2396,6 +2401,7 @@ fn call<'a, 'b: 'a>( for (callee_account_index, caller_account) in accounts.iter_mut() { if let Some(caller_account) = caller_account { let callee_account = invoke_context + .transaction_context .get_account_at_index(*callee_account_index) .borrow(); *caller_account.lamports = callee_account.lamports(); @@ -2669,6 +2675,7 @@ mod tests { }, solana_sdk::{ account::AccountSharedData, bpf_loader, fee_calculator::FeeCalculator, hash::hashv, + transaction_context::TransactionContext, }, std::str::FromStr, }; @@ -2979,9 +2986,11 @@ mod tests { #[should_panic(expected = "UserError(SyscallError(Panic(\"Gaggablaghblagh!\", 42, 84)))")] fn test_syscall_sol_panic() { let program_id = Pubkey::new_unique(); - let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id())); - let accounts = [(program_id, program_account)]; - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); + let 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 syscall_panic = SyscallPanic { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3050,9 +3059,11 @@ mod tests { #[test] fn test_syscall_sol_log() { let program_id = Pubkey::new_unique(); - let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id())); - let accounts = [(program_id, program_account)]; - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); + let 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 syscall_sol_log = SyscallLog { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), @@ -3148,9 +3159,11 @@ mod tests { #[test] fn test_syscall_sol_log_u64() { let program_id = Pubkey::new_unique(); - let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id())); - let accounts = [(program_id, program_account)]; - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); + let 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 cost = invoke_context.get_compute_budget().log_64_units; let mut syscall_sol_log_u64 = SyscallLogU64 { @@ -3184,9 +3197,11 @@ mod tests { #[test] fn test_syscall_sol_pubkey() { let program_id = Pubkey::new_unique(); - let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id())); - let accounts = [(program_id, program_account)]; - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); + let 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 cost = invoke_context.get_compute_budget().log_pubkey_units; let mut syscall_sol_pubkey = SyscallLogPubkey { @@ -3390,10 +3405,14 @@ mod tests { fn test_syscall_sha256() { let config = Config::default(); let program_id = Pubkey::new_unique(); - let program_account = - RefCell::new(AccountSharedData::new(0, 0, &bpf_loader_deprecated::id())); - let accounts = [(program_id, program_account)]; - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); + let 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 bytes1 = "Gaggablaghblagh!"; @@ -3511,11 +3530,55 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_syscall_get_sysvar() { let config = Config::default(); + let src_clock = Clock { + slot: 1, + epoch_start_timestamp: 2, + epoch: 3, + leader_schedule_epoch: 4, + unix_timestamp: 5, + }; + let mut data_clock = vec![]; + bincode::serialize_into(&mut data_clock, &src_clock).unwrap(); + let src_epochschedule = EpochSchedule { + slots_per_epoch: 1, + leader_schedule_slot_offset: 2, + warmup: false, + first_normal_epoch: 3, + first_normal_slot: 4, + }; + let mut data_epochschedule = vec![]; + bincode::serialize_into(&mut data_epochschedule, &src_epochschedule).unwrap(); + let src_fees = Fees { + fee_calculator: FeeCalculator { + lamports_per_signature: 1, + }, + }; + let mut data_fees = vec![]; + bincode::serialize_into(&mut data_fees, &src_fees).unwrap(); + let src_rent = Rent { + lamports_per_byte_year: 1, + exemption_threshold: 2.0, + burn_percent: 3, + }; + let mut data_rent = vec![]; + bincode::serialize_into(&mut data_rent, &src_rent).unwrap(); + let sysvars = [ + (sysvar::clock::id(), data_clock), + (sysvar::epoch_schedule::id(), data_epochschedule), + (sysvar::fees::id(), data_fees), + (sysvar::rent::id(), data_rent), + ]; let program_id = Pubkey::new_unique(); - let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id())); - let accounts = [(program_id, program_account)]; + let 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.sysvars = &sysvars; + invoke_context.push(&[], &[0]).unwrap(); // Test clock sysvar { @@ -3536,20 +3599,6 @@ mod tests { &config, ) .unwrap(); - - let src_clock = Clock { - slot: 1, - epoch_start_timestamp: 2, - epoch: 3, - leader_schedule_epoch: 4, - unix_timestamp: 5, - }; - let mut data = vec![]; - bincode::serialize_into(&mut data, &src_clock).unwrap(); - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); - let sysvars = [(sysvar::clock::id(), data)]; - invoke_context.sysvars = &sysvars; - invoke_context.push(&[], &[0]).unwrap(); let mut syscall = SyscallGetClockSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), }; @@ -3579,20 +3628,6 @@ mod tests { &config, ) .unwrap(); - - let src_epochschedule = EpochSchedule { - slots_per_epoch: 1, - leader_schedule_slot_offset: 2, - warmup: false, - first_normal_epoch: 3, - first_normal_slot: 4, - }; - let mut data = vec![]; - bincode::serialize_into(&mut data, &src_epochschedule).unwrap(); - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); - let sysvars = [(sysvar::epoch_schedule::id(), data)]; - invoke_context.sysvars = &sysvars; - invoke_context.push(&[], &[0]).unwrap(); let mut syscall = SyscallGetEpochScheduleSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), }; @@ -3612,7 +3647,6 @@ mod tests { } // Test fees sysvar - #[allow(deprecated)] { let got_fees = Fees::default(); let got_fees_va = 0x100000000; @@ -3631,18 +3665,6 @@ mod tests { &config, ) .unwrap(); - - let src_fees = Fees { - fee_calculator: FeeCalculator { - lamports_per_signature: 1, - }, - }; - let mut data = vec![]; - bincode::serialize_into(&mut data, &src_fees).unwrap(); - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); - let sysvars = [(sysvar::fees::id(), data)]; - invoke_context.sysvars = &sysvars; - invoke_context.push(&[], &[0]).unwrap(); let mut syscall = SyscallGetFeesSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), }; @@ -3672,18 +3694,6 @@ mod tests { &config, ) .unwrap(); - - let src_rent = Rent { - lamports_per_byte_year: 1, - exemption_threshold: 2.0, - burn_percent: 3, - }; - let mut data = vec![]; - bincode::serialize_into(&mut data, &src_rent).unwrap(); - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); - let sysvars = [(sysvar::rent::id(), data)]; - invoke_context.sysvars = &sysvars; - invoke_context.push(&[], &[0]).unwrap(); let mut syscall = SyscallGetRentSysvar { invoke_context: Rc::new(RefCell::new(&mut invoke_context)), }; @@ -3812,9 +3822,11 @@ mod tests { // These tests duplicate the direct tests in solana_program::pubkey let program_id = Pubkey::new_unique(); - let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id())); - let accounts = [(program_id, program_account)]; - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); + let 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 address = bpf_loader_upgradeable::id(); @@ -3922,9 +3934,11 @@ mod tests { #[test] fn test_find_program_address() { let program_id = Pubkey::new_unique(); - let program_account = RefCell::new(AccountSharedData::new(0, 0, &bpf_loader::id())); - let accounts = [(program_id, program_account)]; - let mut invoke_context = InvokeContext::new_mock(&accounts, &[]); + let 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 cost = invoke_context .get_compute_budget() diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index 50c56f7ae..3986c5ae8 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -1308,6 +1308,7 @@ mod tests { native_token, pubkey::Pubkey, system_program, + transaction_context::TransactionContext, }, solana_vote_program::vote_state, std::{cell::RefCell, iter::FromIterator}, @@ -4998,7 +4999,8 @@ mod tests { #[test] fn test_merge() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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(); @@ -5108,7 +5110,8 @@ mod tests { #[test] fn test_merge_self_fails() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let stake_address = Pubkey::new_unique(); let authority_pubkey = Pubkey::new_unique(); let signers = HashSet::from_iter(vec![authority_pubkey]); @@ -5153,7 +5156,8 @@ mod tests { #[test] fn test_merge_incorrect_authorized_staker() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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(); @@ -5222,7 +5226,8 @@ mod tests { #[test] fn test_merge_invalid_account_data() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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(); @@ -5272,7 +5277,8 @@ mod tests { #[test] fn test_merge_fake_stake_source() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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(); @@ -5314,7 +5320,8 @@ mod tests { #[test] fn test_merge_active_stake() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let base_lamports = 4242424242; let stake_address = Pubkey::new_unique(); let source_address = Pubkey::new_unique(); @@ -5936,7 +5943,8 @@ mod tests { #[test] fn test_things_can_merge() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let good_stake = Stake { credits_observed: 4242, delegation: Delegation { @@ -6034,7 +6042,8 @@ mod tests { #[test] fn test_metas_can_merge_pre_v4() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); // Identical Metas can merge assert!(MergeKind::metas_can_merge( &invoke_context, @@ -6120,7 +6129,8 @@ mod tests { #[test] fn test_metas_can_merge_v4() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); // Identical Metas can merge assert!(MergeKind::metas_can_merge( &invoke_context, @@ -6266,7 +6276,8 @@ mod tests { #[test] fn test_merge_kind_get_if_mergeable() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let authority_pubkey = Pubkey::new_unique(); let initial_lamports = 4242424242; let rent = Rent::default(); @@ -6498,7 +6509,8 @@ mod tests { #[test] fn test_merge_kind_merge() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let lamports = 424242; let meta = Meta { rent_exempt_reserve: 42, @@ -6576,7 +6588,8 @@ mod tests { #[test] fn test_active_stake_merge() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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 ba73371f6..e43939d31 100644 --- a/rbpf-cli/src/main.rs +++ b/rbpf-cli/src/main.rs @@ -16,6 +16,7 @@ use { }, solana_sdk::{ account::AccountSharedData, bpf_loader, instruction::AccountMeta, pubkey::Pubkey, + transaction_context::TransactionContext, }, std::{ fs::File, @@ -210,7 +211,8 @@ native machine code before execting it in the virtual machine.", }; let preparation = prepare_mock_invoke_context(transaction_accounts, instruction_accounts); let program_indices = [0, 1]; - let mut invoke_context = InvokeContext::new_mock(&preparation.transaction_accounts, &[]); + let transaction_context = TransactionContext::new(preparation.transaction_accounts, 1); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); invoke_context .push(&preparation.instruction_accounts, &program_indices) .unwrap(); diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index fe9f77d93..9b562b36b 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -37,6 +37,7 @@ use { system_program, sysvar::{self, instructions::construct_instructions_data}, transaction::{Result, SanitizedTransaction, TransactionError}, + transaction_context::TransactionAccount, }, std::{ cmp::Reverse, @@ -105,12 +106,11 @@ pub struct Accounts { } // for the load instructions -pub type TransactionAccounts = Vec<(Pubkey, AccountSharedData)>; pub type TransactionRent = u64; pub type TransactionProgramIndices = Vec>; #[derive(PartialEq, Debug, Clone)] pub struct LoadedTransaction { - pub accounts: TransactionAccounts, + pub accounts: Vec, pub program_indices: TransactionProgramIndices, pub rent: TransactionRent, pub rent_debits: RentDebits, @@ -385,7 +385,7 @@ impl Accounts { fn load_executable_accounts( &self, ancestors: &Ancestors, - accounts: &mut Vec<(Pubkey, AccountSharedData)>, + accounts: &mut Vec, mut program_account_index: usize, error_counters: &mut ErrorCounters, ) -> Result> { @@ -606,7 +606,7 @@ impl Accounts { &self, slot: Slot, program_id: Option<&Pubkey>, - ) -> Vec<(Pubkey, AccountSharedData)> { + ) -> Vec { self.scan_slot(slot, |stored_account| { let hit = match program_id { None => true, @@ -721,7 +721,7 @@ impl Accounts { } fn load_while_filtering bool>( - collector: &mut Vec<(Pubkey, AccountSharedData)>, + collector: &mut Vec, some_account_tuple: Option<(&Pubkey, AccountSharedData, Slot)>, filter: F, ) { @@ -739,11 +739,11 @@ impl Accounts { bank_id: BankId, program_id: &Pubkey, config: &ScanConfig, - ) -> ScanResult> { + ) -> ScanResult> { self.accounts_db.scan_accounts( ancestors, bank_id, - |collector: &mut Vec<(Pubkey, AccountSharedData)>, some_account_tuple| { + |collector: &mut Vec, some_account_tuple| { Self::load_while_filtering(collector, some_account_tuple, |account| { account.owner() == program_id }) @@ -759,11 +759,11 @@ impl Accounts { program_id: &Pubkey, filter: F, config: &ScanConfig, - ) -> ScanResult> { + ) -> ScanResult> { self.accounts_db.scan_accounts( ancestors, bank_id, - |collector: &mut Vec<(Pubkey, AccountSharedData)>, some_account_tuple| { + |collector: &mut Vec, some_account_tuple| { Self::load_while_filtering(collector, some_account_tuple, |account| { account.owner() == program_id && filter(account) }) @@ -796,9 +796,9 @@ impl Accounts { } fn maybe_abort_scan( - result: ScanResult>, + result: ScanResult>, config: &ScanConfig, - ) -> ScanResult> { + ) -> ScanResult> { if config.is_aborted() { ScanResult::Err(ScanError::Aborted( "The accumulated scan results exceeded the limit".to_string(), @@ -816,7 +816,7 @@ impl Accounts { filter: F, config: &ScanConfig, byte_limit_for_scan: Option, - ) -> ScanResult> { + ) -> ScanResult> { let sum = AtomicUsize::default(); let config = config.recreate_with_abort(); let result = self @@ -825,7 +825,7 @@ impl Accounts { ancestors, bank_id, *index_key, - |collector: &mut Vec<(Pubkey, AccountSharedData)>, some_account_tuple| { + |collector: &mut Vec, some_account_tuple| { Self::load_while_filtering(collector, some_account_tuple, |account| { let use_account = filter(account); if use_account @@ -887,13 +887,13 @@ impl Accounts { &self, ancestors: &Ancestors, range: R, - ) -> Vec<(Pubkey, AccountSharedData)> { + ) -> Vec { self.accounts_db.range_scan_accounts( "load_to_collect_rent_eagerly_scan_elapsed", ancestors, range, &ScanConfig::new(true), - |collector: &mut Vec<(Pubkey, AccountSharedData)>, option| { + |collector: &mut Vec, option| { Self::load_while_filtering(collector, option, |_| true) }, ) @@ -1277,7 +1277,7 @@ mod tests { fn load_accounts_with_fee_and_rent( tx: Transaction, - ka: &[(Pubkey, AccountSharedData)], + ka: &[TransactionAccount], lamports_per_signature: u64, rent_collector: &RentCollector, error_counters: &mut ErrorCounters, @@ -1310,7 +1310,7 @@ mod tests { fn load_accounts_with_fee( tx: Transaction, - ka: &[(Pubkey, AccountSharedData)], + ka: &[TransactionAccount], lamports_per_signature: u64, error_counters: &mut ErrorCounters, ) -> Vec { @@ -1325,7 +1325,7 @@ mod tests { fn load_accounts( tx: Transaction, - ka: &[(Pubkey, AccountSharedData)], + ka: &[TransactionAccount], error_counters: &mut ErrorCounters, ) -> Vec { load_accounts_with_fee(tx, ka, 0, error_counters) @@ -1390,7 +1390,7 @@ mod tests { #[test] fn test_load_accounts_no_account_0_exists() { - let accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -1416,7 +1416,7 @@ mod tests { #[test] fn test_load_accounts_unknown_program_id() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -1450,7 +1450,7 @@ mod tests { #[test] fn test_load_accounts_insufficient_funds() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -1486,7 +1486,7 @@ mod tests { #[test] fn test_load_accounts_invalid_account_for_fee() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -1588,7 +1588,7 @@ mod tests { #[test] fn test_load_accounts_no_loaders() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -1629,7 +1629,7 @@ mod tests { #[test] fn test_load_accounts_max_call_depth() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -1695,7 +1695,7 @@ mod tests { #[test] fn test_load_accounts_bad_owner() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -1730,7 +1730,7 @@ mod tests { #[test] fn test_load_accounts_not_executable() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -1764,7 +1764,7 @@ mod tests { #[test] fn test_load_accounts_multiple_loaders() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -1860,7 +1860,7 @@ mod tests { #[test] fn test_load_accounts_executable_with_write_lock() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -1916,7 +1916,7 @@ mod tests { #[test] fn test_load_accounts_upgradeable_with_write_lock() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); @@ -2013,7 +2013,7 @@ mod tests { #[test] fn test_load_accounts_programdata_with_write_lock() { - let mut accounts: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + let mut accounts: Vec = Vec::new(); let mut error_counters = ErrorCounters::default(); let keypair = Keypair::new(); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 4d5a059e8..e61d1f934 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -37,7 +37,7 @@ use solana_sdk::recent_blockhashes_account; use { crate::{ - accounts::{AccountAddressFilter, Accounts, TransactionAccounts, TransactionLoadResult}, + accounts::{AccountAddressFilter, Accounts, TransactionLoadResult}, accounts_db::{ AccountShrinkThreshold, AccountsDbConfig, ErrorCounters, SnapshotStorages, ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING, @@ -74,10 +74,7 @@ use { solana_metrics::{inc_new_counter_debug, inc_new_counter_info}, solana_program_runtime::{ instruction_recorder::InstructionRecorder, - invoke_context::{ - BuiltinProgram, Executor, Executors, ProcessInstructionWithContext, - TransactionAccountRefCells, - }, + invoke_context::{BuiltinProgram, Executor, Executors, ProcessInstructionWithContext}, log_collector::LogCollector, timings::ExecuteDetailsTimings, }, @@ -127,6 +124,7 @@ use { Result, SanitizedTransaction, Transaction, TransactionError, TransactionVerificationMode, VersionedTransaction, }, + transaction_context::{TransactionAccount, TransactionContext}, }, solana_stake_program::stake_state::{ self, InflationPointCalculationEvent, PointValue, StakeState, @@ -515,7 +513,7 @@ pub struct TransactionResults { pub struct TransactionSimulationResult { pub result: Result<()>, pub logs: TransactionLogMessages, - pub post_simulation_accounts: Vec<(Pubkey, AccountSharedData)>, + pub post_simulation_accounts: Vec, pub units_consumed: u64, } pub struct TransactionBalancesSet { @@ -654,7 +652,7 @@ impl NonceFull { pub fn from_partial( partial: NoncePartial, message: &SanitizedMessage, - accounts: &[(Pubkey, AccountSharedData)], + accounts: &[TransactionAccount], rent_debits: &RentDebits, ) -> Result { let fee_payer = (0..message.account_keys_len()).find_map(|i| { @@ -3287,7 +3285,7 @@ impl Bank { pub fn check_transaction_for_nonce( &self, tx: &SanitizedTransaction, - ) -> Option<(Pubkey, AccountSharedData)> { + ) -> Option { tx.get_durable_nonce(self.feature_set.is_active(&nonce_must_be_writable::id())) .and_then(|nonce_address| { self.get_account_with_fixed_root(nonce_address) @@ -3404,31 +3402,11 @@ impl Bank { } } - /// Converts Accounts into RefCell, this involves moving - /// ownership by draining the source - fn accounts_to_refcells(accounts: &mut TransactionAccounts) -> TransactionAccountRefCells { - accounts - .drain(..) - .map(|(pubkey, account)| (pubkey, RefCell::new(account))) - .collect() - } - - /// Converts back from RefCell to AccountSharedData, this involves moving - /// ownership by draining the sources - fn refcells_to_accounts( - accounts: &mut TransactionAccounts, - mut account_refcells: TransactionAccountRefCells, - ) { - for (pubkey, account_refcell) in account_refcells.drain(..) { - accounts.push((pubkey, account_refcell.into_inner())) - } - } - /// Get any cached executors needed by the transaction fn get_executors( &self, message: &SanitizedMessage, - accounts: &[(Pubkey, AccountSharedData)], + accounts: &[TransactionAccount], program_indices: &[Vec], ) -> Rc> { let mut num_executors = message.account_keys_len(); @@ -3573,8 +3551,12 @@ impl Bank { &loaded_transaction.program_indices, ); - let account_refcells = - Self::accounts_to_refcells(&mut loaded_transaction.accounts); + let mut transaction_accounts = Vec::new(); + std::mem::swap(&mut loaded_transaction.accounts, &mut transaction_accounts); + let transaction_context = TransactionContext::new( + transaction_accounts, + compute_budget.max_invoke_depth, + ); let instruction_recorder = if enable_cpi_recording { Some(InstructionRecorder::new_ref( @@ -3598,7 +3580,7 @@ impl Bank { &self.builtin_programs.vec, legacy_message, &loaded_transaction.program_indices, - &account_refcells, + &transaction_context, self.rent_collector.rent, log_collector.clone(), executors.clone(), @@ -3637,10 +3619,7 @@ impl Bank { }), ); - Self::refcells_to_accounts( - &mut loaded_transaction.accounts, - account_refcells, - ); + loaded_transaction.accounts = transaction_context.deconstruct(); if process_result.is_ok() { self.update_executors(executors); @@ -4993,7 +4972,7 @@ impl Bank { &self, program_id: &Pubkey, config: &ScanConfig, - ) -> ScanResult> { + ) -> ScanResult> { self.rc .accounts .load_by_program(&self.ancestors, self.bank_id, program_id, config) @@ -5004,7 +4983,7 @@ impl Bank { program_id: &Pubkey, filter: F, config: &ScanConfig, - ) -> ScanResult> { + ) -> ScanResult> { self.rc.accounts.load_by_program_with_filter( &self.ancestors, self.bank_id, @@ -5020,7 +4999,7 @@ impl Bank { filter: F, config: &ScanConfig, byte_limit_for_scan: Option, - ) -> ScanResult> { + ) -> ScanResult> { self.rc.accounts.load_by_index_key_with_filter( &self.ancestors, self.bank_id, @@ -5044,7 +5023,7 @@ impl Bank { pub fn get_program_accounts_modified_since_parent( &self, program_id: &Pubkey, - ) -> Vec<(Pubkey, AccountSharedData)> { + ) -> Vec { self.rc .accounts .load_by_program_slot(self.slot(), Some(program_id)) @@ -5060,7 +5039,7 @@ impl Bank { .get_logs_for_address(address) } - pub fn get_all_accounts_modified_since_parent(&self) -> Vec<(Pubkey, AccountSharedData)> { + pub fn get_all_accounts_modified_since_parent(&self) -> Vec { self.rc.accounts.load_by_program_slot(self.slot(), None) } @@ -6722,8 +6701,7 @@ pub(crate) mod tests { mock_program_id: Pubkey, generic_rent_due_for_system_account: u64, ) { - let mut account_pairs: Vec<(Pubkey, AccountSharedData)> = - Vec::with_capacity(keypairs.len() - 1); + let mut account_pairs: Vec = Vec::with_capacity(keypairs.len() - 1); account_pairs.push(( keypairs[0].pubkey(), AccountSharedData::new( diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 35c97923b..d3bb5f297 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -3,9 +3,7 @@ use { solana_measure::measure::Measure, solana_program_runtime::{ instruction_recorder::InstructionRecorder, - invoke_context::{ - BuiltinProgram, Executors, InstructionAccount, InvokeContext, TransactionAccountRefCell, - }, + invoke_context::{BuiltinProgram, Executors, InvokeContext}, log_collector::LogCollector, timings::ExecuteDetailsTimings, }, @@ -20,6 +18,7 @@ use { rent::Rent, sysvar::instructions, transaction::TransactionError, + transaction_context::{InstructionAccount, TransactionContext}, }, std::{cell::RefCell, rc::Rc, sync::Arc}, }; @@ -54,7 +53,7 @@ impl MessageProcessor { builtin_programs: &[BuiltinProgram], message: &Message, program_indices: &[Vec], - accounts: &[TransactionAccountRefCell], + transaction_context: &TransactionContext, rent: Rent, log_collector: Option>>, executors: Rc>, @@ -67,8 +66,8 @@ impl MessageProcessor { lamports_per_signature: u64, ) -> Result { let mut invoke_context = InvokeContext::new( + transaction_context, rent, - accounts, builtin_programs, sysvars, log_collector, @@ -99,15 +98,18 @@ impl MessageProcessor { // Fixup the special instructions key if present // before the account pre-values are taken care of - for (pubkey, account) in accounts.iter().take(message.account_keys.len()) { - if instructions::check_id(pubkey) { - let mut mut_account_ref = account.borrow_mut(); - instructions::store_current_index( - mut_account_ref.data_as_mut_slice(), - instruction_index as u16, - ); - break; - } + if let Some(account_index) = invoke_context + .transaction_context + .find_index_of_account(&instructions::id()) + { + let mut mut_account_ref = invoke_context + .transaction_context + .get_account_at_index(account_index) + .borrow_mut(); + instructions::store_current_index( + mut_account_ref.data_as_mut_slice(), + instruction_index as u16, + ); } let instruction_accounts = instruction @@ -221,39 +223,38 @@ mod tests { let accounts = vec![ ( solana_sdk::pubkey::new_rand(), - RefCell::new(AccountSharedData::new(100, 1, &mock_system_program_id)), + AccountSharedData::new(100, 1, &mock_system_program_id), ), ( solana_sdk::pubkey::new_rand(), - RefCell::new(AccountSharedData::new(0, 1, &mock_system_program_id)), + AccountSharedData::new(0, 1, &mock_system_program_id), ), ( mock_system_program_id, - RefCell::new(create_loadable_account_for_test("mock_system_program")), + create_loadable_account_for_test("mock_system_program"), ), ]; + let transaction_context = TransactionContext::new(accounts, 1); let program_indices = vec![vec![2]]; - let executors = Rc::new(RefCell::new(Executors::default())); - let account_metas = vec![ - AccountMeta::new(accounts[0].0, true), - AccountMeta::new_readonly(accounts[1].0, false), + AccountMeta::new(*transaction_context.get_key_of_account_at_index(0), true), + AccountMeta::new_readonly(*transaction_context.get_key_of_account_at_index(1), false), ]; + let message = Message::new( &[Instruction::new_with_bincode( mock_system_program_id, &MockSystemInstruction::Correct, account_metas.clone(), )], - Some(&accounts[0].0), + Some(transaction_context.get_key_of_account_at_index(0)), ); - let result = MessageProcessor::process_message( builtin_programs, &message, &program_indices, - &accounts, + &transaction_context, rent_collector.rent, None, executors.clone(), @@ -266,8 +267,20 @@ mod tests { 0, ); assert!(result.is_ok()); - assert_eq!(accounts[0].1.borrow().lamports(), 100); - assert_eq!(accounts[1].1.borrow().lamports(), 0); + assert_eq!( + transaction_context + .get_account_at_index(0) + .borrow() + .lamports(), + 100 + ); + assert_eq!( + transaction_context + .get_account_at_index(1) + .borrow() + .lamports(), + 0 + ); let message = Message::new( &[Instruction::new_with_bincode( @@ -275,14 +288,13 @@ mod tests { &MockSystemInstruction::AttemptCredit { lamports: 50 }, account_metas.clone(), )], - Some(&accounts[0].0), + Some(transaction_context.get_key_of_account_at_index(0)), ); - let result = MessageProcessor::process_message( builtin_programs, &message, &program_indices, - &accounts, + &transaction_context, rent_collector.rent, None, executors.clone(), @@ -308,14 +320,13 @@ mod tests { &MockSystemInstruction::AttemptDataChange { data: 50 }, account_metas, )], - Some(&accounts[0].0), + Some(transaction_context.get_key_of_account_at_index(0)), ); - let result = MessageProcessor::process_message( builtin_programs, &message, &program_indices, - &accounts, + &transaction_context, rent_collector.rent, None, executors, @@ -425,25 +436,24 @@ mod tests { let accounts = vec![ ( solana_sdk::pubkey::new_rand(), - RefCell::new(AccountSharedData::new(100, 1, &mock_program_id)), + AccountSharedData::new(100, 1, &mock_program_id), ), ( solana_sdk::pubkey::new_rand(), - RefCell::new(AccountSharedData::new(0, 1, &mock_program_id)), + AccountSharedData::new(0, 1, &mock_program_id), ), ( mock_program_id, - RefCell::new(create_loadable_account_for_test("mock_system_program")), + create_loadable_account_for_test("mock_system_program"), ), ]; + let transaction_context = TransactionContext::new(accounts, 1); let program_indices = vec![vec![2]]; - let executors = Rc::new(RefCell::new(Executors::default())); - let account_metas = vec![ - AccountMeta::new(accounts[0].0, true), - AccountMeta::new(accounts[1].0, false), - AccountMeta::new(accounts[0].0, false), + AccountMeta::new(*transaction_context.get_key_of_account_at_index(0), true), + AccountMeta::new(*transaction_context.get_key_of_account_at_index(1), false), + AccountMeta::new(*transaction_context.get_key_of_account_at_index(0), false), ]; // Try to borrow mut the same account @@ -453,13 +463,13 @@ mod tests { &MockSystemInstruction::BorrowFail, account_metas.clone(), )], - Some(&accounts[0].0), + Some(transaction_context.get_key_of_account_at_index(0)), ); let result = MessageProcessor::process_message( builtin_programs, &message, &program_indices, - &accounts, + &transaction_context, rent_collector.rent, None, executors.clone(), @@ -486,13 +496,13 @@ mod tests { &MockSystemInstruction::MultiBorrowMut, account_metas.clone(), )], - Some(&accounts[0].0), + Some(transaction_context.get_key_of_account_at_index(0)), ); let result = MessageProcessor::process_message( builtin_programs, &message, &program_indices, - &accounts, + &transaction_context, rent_collector.rent, None, executors.clone(), @@ -516,13 +526,13 @@ mod tests { }, account_metas, )], - Some(&accounts[0].0), + Some(transaction_context.get_key_of_account_at_index(0)), ); let result = MessageProcessor::process_message( builtin_programs, &message, &program_indices, - &accounts, + &transaction_context, rent_collector.rent, None, executors, @@ -535,9 +545,24 @@ mod tests { 0, ); assert!(result.is_ok()); - assert_eq!(accounts[0].1.borrow().lamports(), 80); - assert_eq!(accounts[1].1.borrow().lamports(), 20); - assert_eq!(accounts[0].1.borrow().data(), &vec![42]); + assert_eq!( + transaction_context + .get_account_at_index(0) + .borrow() + .lamports(), + 80 + ); + assert_eq!( + transaction_context + .get_account_at_index(1) + .borrow() + .lamports(), + 20 + ); + assert_eq!( + transaction_context.get_account_at_index(0).borrow().data(), + &vec![42] + ); } #[test] @@ -560,9 +585,10 @@ mod tests { let mut mock_program_account = AccountSharedData::new(1, 0, &native_loader::id()); mock_program_account.set_executable(true); let accounts = vec![ - (secp256k1_program::id(), RefCell::new(secp256k1_account)), - (mock_program_id, RefCell::new(mock_program_account)), + (secp256k1_program::id(), secp256k1_account), + (mock_program_id, mock_program_account), ]; + let transaction_context = TransactionContext::new(accounts, 1); let message = Message::new( &[ @@ -574,12 +600,11 @@ mod tests { ], None, ); - let result = MessageProcessor::process_message( builtin_programs, &message, &[vec![0], vec![1]], - &accounts, + &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 e154d86fe..d700fdff4 100644 --- a/runtime/src/nonce_keyed_account.rs +++ b/runtime/src/nonce_keyed_account.rs @@ -326,32 +326,25 @@ mod test { nonce::{self, State}, nonce_account::{create_account, verify_nonce_account}, system_instruction::SystemError, + transaction_context::TransactionContext, }, }; - fn with_test_keyed_account(lamports: u64, signer: bool, f: F) + fn with_mockup(lamports: u64, signer: bool, mut f: F) where - F: Fn(&KeyedAccount), + F: FnMut(&mut InvokeContext, &KeyedAccount), { + let transaction_context = TransactionContext::new(Vec::new(), 1); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let pubkey = Pubkey::new_unique(); let account = create_account(lamports); let keyed_account = KeyedAccount::new(&pubkey, signer, &account); - f(&keyed_account) + f(&mut invoke_context, &keyed_account) } - fn create_test_blockhash(seed: usize) -> (Hash, u64) { - ( - hash(&bincode::serialize(&seed).unwrap()), - (seed as u64).saturating_mul(100), - ) - } - - fn create_invoke_context_with_blockhash<'a>(seed: usize) -> InvokeContext<'a> { - let mut invoke_context = InvokeContext::new_mock(&[], &[]); - let (blockhash, lamports_per_signature) = create_test_blockhash(seed); - invoke_context.blockhash = blockhash; - invoke_context.lamports_per_signature = lamports_per_signature; - invoke_context + fn set_invoke_context_blockhash(invoke_context: &mut InvokeContext, seed: usize) { + invoke_context.blockhash = hash(&bincode::serialize(&seed).unwrap()); + invoke_context.lamports_per_signature = (seed as u64).saturating_mul(100); } #[test] @@ -366,7 +359,7 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |keyed_account| { + with_mockup(min_lamports + 42, true, |invoke_context, keyed_account| { let data = nonce::state::Data { authority: *keyed_account.unsigned_key(), ..nonce::state::Data::default() @@ -378,10 +371,10 @@ mod test { .convert_to_current(); // New is in Uninitialzed state assert_eq!(state, State::Uninitialized); - let invoke_context = create_invoke_context_with_blockhash(95); + set_invoke_context_blockhash(invoke_context, 95); let authorized = keyed_account.unsigned_key(); keyed_account - .initialize_nonce_account(authorized, &rent, &invoke_context) + .initialize_nonce_account(authorized, &rent, invoke_context) .unwrap(); let state = AccountUtilsState::::state(keyed_account) .unwrap() @@ -393,9 +386,9 @@ mod test { ); // First nonce instruction drives state from Uninitialized to Initialized assert_eq!(state, State::Initialized(data.clone())); - let invoke_context = create_invoke_context_with_blockhash(63); + set_invoke_context_blockhash(invoke_context, 63); keyed_account - .advance_nonce_account(&signers, &invoke_context) + .advance_nonce_account(&signers, invoke_context) .unwrap(); let state = AccountUtilsState::::state(keyed_account) .unwrap() @@ -407,9 +400,9 @@ mod test { ); // Second nonce instruction consumes and replaces stored nonce assert_eq!(state, State::Initialized(data.clone())); - let invoke_context = create_invoke_context_with_blockhash(31); + set_invoke_context_blockhash(invoke_context, 31); keyed_account - .advance_nonce_account(&signers, &invoke_context) + .advance_nonce_account(&signers, invoke_context) .unwrap(); let state = AccountUtilsState::::state(keyed_account) .unwrap() @@ -421,8 +414,8 @@ mod test { ); // Third nonce instruction for fun and profit assert_eq!(state, State::Initialized(data)); - with_test_keyed_account(42, false, |to_keyed| { - let invoke_context = create_invoke_context_with_blockhash(0); + with_mockup(42, false, |_invoke_context, to_keyed| { + set_invoke_context_blockhash(invoke_context, 0); let withdraw_lamports = keyed_account.account.borrow().lamports(); let expect_nonce_lamports = keyed_account.account.borrow().lamports() - withdraw_lamports; @@ -433,7 +426,7 @@ mod test { to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ) .unwrap(); // Empties Account balance @@ -459,11 +452,11 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_account| { - let invoke_context = create_invoke_context_with_blockhash(31); + with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| { + set_invoke_context_blockhash(invoke_context, 31); let authority = *nonce_account.unsigned_key(); nonce_account - .initialize_nonce_account(&authority, &rent, &invoke_context) + .initialize_nonce_account(&authority, &rent, invoke_context) .unwrap(); let pubkey = *nonce_account.account.borrow().owner(); let nonce_account = KeyedAccount::new(&pubkey, false, nonce_account.account); @@ -477,9 +470,9 @@ mod test { ); assert_eq!(state, State::Initialized(data)); let signers = HashSet::new(); - let invoke_context = create_invoke_context_with_blockhash(0); + set_invoke_context_blockhash(invoke_context, 0); - let result = nonce_account.advance_nonce_account(&signers, &invoke_context); + let result = nonce_account.advance_nonce_account(&signers, invoke_context); assert_eq!(result, Err(InstructionError::MissingRequiredSignature),); }) } @@ -491,15 +484,15 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |keyed_account| { + with_mockup(min_lamports + 42, true, |invoke_context, keyed_account| { let mut signers = HashSet::new(); signers.insert(*keyed_account.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(63); + set_invoke_context_blockhash(invoke_context, 63); let authorized = *keyed_account.unsigned_key(); keyed_account - .initialize_nonce_account(&authorized, &rent, &invoke_context) + .initialize_nonce_account(&authorized, &rent, invoke_context) .unwrap(); - let result = keyed_account.advance_nonce_account(&signers, &invoke_context); + let result = keyed_account.advance_nonce_account(&signers, invoke_context); assert_eq!(result, Err(SystemError::NonceBlockhashNotExpired.into())); }) } @@ -511,11 +504,11 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |keyed_account| { + with_mockup(min_lamports + 42, true, |invoke_context, keyed_account| { let mut signers = HashSet::new(); signers.insert(*keyed_account.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(63); - let result = keyed_account.advance_nonce_account(&signers, &invoke_context); + set_invoke_context_blockhash(invoke_context, 63); + let result = keyed_account.advance_nonce_account(&signers, invoke_context); assert_eq!(result, Err(InstructionError::InvalidAccountData)); }) } @@ -527,19 +520,19 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_account| { - with_test_keyed_account(42, true, |nonce_authority| { + with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| { + with_mockup(42, true, |_invoke_context, nonce_authority| { let mut signers = HashSet::new(); signers.insert(*nonce_account.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(63); + set_invoke_context_blockhash(invoke_context, 63); let authorized = *nonce_authority.unsigned_key(); nonce_account - .initialize_nonce_account(&authorized, &rent, &invoke_context) + .initialize_nonce_account(&authorized, &rent, invoke_context) .unwrap(); let mut signers = HashSet::new(); signers.insert(*nonce_authority.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(31); - let result = nonce_account.advance_nonce_account(&signers, &invoke_context); + set_invoke_context_blockhash(invoke_context, 31); + let result = nonce_account.advance_nonce_account(&signers, invoke_context); assert_eq!(result, Ok(())); }); }); @@ -552,16 +545,16 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_account| { - with_test_keyed_account(42, false, |nonce_authority| { + with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| { + with_mockup(42, false, |_invoke_context, nonce_authority| { let mut signers = HashSet::new(); signers.insert(*nonce_account.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(63); + set_invoke_context_blockhash(invoke_context, 63); let authorized = *nonce_authority.unsigned_key(); nonce_account - .initialize_nonce_account(&authorized, &rent, &invoke_context) + .initialize_nonce_account(&authorized, &rent, invoke_context) .unwrap(); - let result = nonce_account.advance_nonce_account(&signers, &invoke_context); + let result = nonce_account.advance_nonce_account(&signers, invoke_context); assert_eq!(result, Err(InstructionError::MissingRequiredSignature),); }); }); @@ -574,15 +567,15 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| { + with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| { let state = AccountUtilsState::::state(nonce_keyed) .unwrap() .convert_to_current(); assert_eq!(state, State::Uninitialized); - with_test_keyed_account(42, false, |to_keyed| { + with_mockup(42, false, |_invoke_context, to_keyed| { let mut signers = HashSet::new(); signers.insert(*nonce_keyed.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(0); + set_invoke_context_blockhash(invoke_context, 0); let withdraw_lamports = nonce_keyed.account.borrow().lamports(); let expect_nonce_lamports = nonce_keyed.account.borrow().lamports() - withdraw_lamports; @@ -593,7 +586,7 @@ mod test { to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -620,21 +613,21 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, false, |nonce_keyed| { + with_mockup(min_lamports + 42, false, |invoke_context, nonce_keyed| { let state = AccountUtilsState::::state(nonce_keyed) .unwrap() .convert_to_current(); assert_eq!(state, State::Uninitialized); - with_test_keyed_account(42, false, |to_keyed| { + with_mockup(42, false, |_invoke_context, to_keyed| { let signers = HashSet::new(); - let invoke_context = create_invoke_context_with_blockhash(0); + set_invoke_context_blockhash(invoke_context, 0); let lamports = nonce_keyed.account.borrow().lamports(); let result = nonce_keyed.withdraw_nonce_account( lamports, to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ); assert_eq!(result, Err(InstructionError::MissingRequiredSignature),); }) @@ -648,22 +641,22 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| { + with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| { let state = AccountUtilsState::::state(nonce_keyed) .unwrap() .convert_to_current(); assert_eq!(state, State::Uninitialized); - with_test_keyed_account(42, false, |to_keyed| { + with_mockup(42, false, |_invoke_context, to_keyed| { let mut signers = HashSet::new(); signers.insert(*nonce_keyed.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(0); + set_invoke_context_blockhash(invoke_context, 0); let lamports = nonce_keyed.account.borrow().lamports() + 1; let result = nonce_keyed.withdraw_nonce_account( lamports, to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ); assert_eq!(result, Err(InstructionError::InsufficientFunds)); }) @@ -677,11 +670,11 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| { - with_test_keyed_account(42, false, |to_keyed| { + with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| { + with_mockup(42, false, |_invoke_context, to_keyed| { let mut signers = HashSet::new(); signers.insert(*nonce_keyed.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(0); + set_invoke_context_blockhash(invoke_context, 0); let withdraw_lamports = nonce_keyed.account.borrow().lamports() / 2; let nonce_expect_lamports = nonce_keyed.account.borrow().lamports() - withdraw_lamports; @@ -692,7 +685,7 @@ mod test { to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -714,7 +707,7 @@ mod test { to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -737,13 +730,13 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| { + with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| { let mut signers = HashSet::new(); signers.insert(*nonce_keyed.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(31); + set_invoke_context_blockhash(invoke_context, 31); let authority = *nonce_keyed.unsigned_key(); nonce_keyed - .initialize_nonce_account(&authority, &rent, &invoke_context) + .initialize_nonce_account(&authority, &rent, invoke_context) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) .unwrap() @@ -754,7 +747,7 @@ mod test { invoke_context.lamports_per_signature, ); assert_eq!(state, State::Initialized(data.clone())); - with_test_keyed_account(42, false, |to_keyed| { + with_mockup(42, false, |_invoke_context, to_keyed| { let withdraw_lamports = nonce_keyed.account.borrow().lamports() - min_lamports; let nonce_expect_lamports = nonce_keyed.account.borrow().lamports() - withdraw_lamports; @@ -765,7 +758,7 @@ mod test { to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -782,7 +775,7 @@ mod test { nonce_expect_lamports ); assert_eq!(to_keyed.account.borrow().lamports(), to_expect_lamports); - let invoke_context = create_invoke_context_with_blockhash(0); + set_invoke_context_blockhash(invoke_context, 0); let withdraw_lamports = nonce_keyed.account.borrow().lamports(); let nonce_expect_lamports = nonce_keyed.account.borrow().lamports() - withdraw_lamports; @@ -793,7 +786,7 @@ mod test { to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ) .unwrap(); let state = AccountUtilsState::::state(nonce_keyed) @@ -816,13 +809,13 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| { - let invoke_context = create_invoke_context_with_blockhash(0); + with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| { + set_invoke_context_blockhash(invoke_context, 0); let authorized = *nonce_keyed.unsigned_key(); nonce_keyed - .initialize_nonce_account(&authorized, &rent, &invoke_context) + .initialize_nonce_account(&authorized, &rent, invoke_context) .unwrap(); - with_test_keyed_account(42, false, |to_keyed| { + with_mockup(42, false, |_invoke_context, to_keyed| { let mut signers = HashSet::new(); signers.insert(*nonce_keyed.signer_key().unwrap()); let withdraw_lamports = nonce_keyed.account.borrow().lamports(); @@ -831,7 +824,7 @@ mod test { to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ); assert_eq!(result, Err(SystemError::NonceBlockhashNotExpired.into())); }) @@ -845,14 +838,14 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| { - let invoke_context = create_invoke_context_with_blockhash(95); + with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| { + set_invoke_context_blockhash(invoke_context, 95); let authorized = *nonce_keyed.unsigned_key(); nonce_keyed - .initialize_nonce_account(&authorized, &rent, &invoke_context) + .initialize_nonce_account(&authorized, &rent, invoke_context) .unwrap(); - with_test_keyed_account(42, false, |to_keyed| { - let invoke_context = create_invoke_context_with_blockhash(63); + with_mockup(42, false, |_invoke_context, to_keyed| { + set_invoke_context_blockhash(invoke_context, 63); let mut signers = HashSet::new(); signers.insert(*nonce_keyed.signer_key().unwrap()); let withdraw_lamports = nonce_keyed.account.borrow().lamports() + 1; @@ -861,7 +854,7 @@ mod test { to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ); assert_eq!(result, Err(InstructionError::InsufficientFunds)); }) @@ -875,14 +868,14 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| { - let invoke_context = create_invoke_context_with_blockhash(95); + with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| { + set_invoke_context_blockhash(invoke_context, 95); let authorized = *nonce_keyed.unsigned_key(); nonce_keyed - .initialize_nonce_account(&authorized, &rent, &invoke_context) + .initialize_nonce_account(&authorized, &rent, invoke_context) .unwrap(); - with_test_keyed_account(42, false, |to_keyed| { - let invoke_context = create_invoke_context_with_blockhash(63); + with_mockup(42, false, |_invoke_context, to_keyed| { + set_invoke_context_blockhash(invoke_context, 63); let mut signers = HashSet::new(); signers.insert(*nonce_keyed.signer_key().unwrap()); let withdraw_lamports = nonce_keyed.account.borrow().lamports() - min_lamports + 1; @@ -891,7 +884,7 @@ mod test { to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ); assert_eq!(result, Err(InstructionError::InsufficientFunds)); }) @@ -905,14 +898,14 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_keyed| { - let invoke_context = create_invoke_context_with_blockhash(95); + with_mockup(min_lamports + 42, true, |invoke_context, nonce_keyed| { + set_invoke_context_blockhash(invoke_context, 95); let authorized = *nonce_keyed.unsigned_key(); nonce_keyed - .initialize_nonce_account(&authorized, &rent, &invoke_context) + .initialize_nonce_account(&authorized, &rent, invoke_context) .unwrap(); - with_test_keyed_account(55, false, |to_keyed| { - let invoke_context = create_invoke_context_with_blockhash(63); + with_mockup(55, false, |_invoke_context, to_keyed| { + set_invoke_context_blockhash(invoke_context, 63); let mut signers = HashSet::new(); signers.insert(*nonce_keyed.signer_key().unwrap()); let withdraw_lamports = u64::MAX - 54; @@ -921,7 +914,7 @@ mod test { to_keyed, &rent, &signers, - &invoke_context, + invoke_context, ); assert_eq!(result, Err(InstructionError::InsufficientFunds)); }) @@ -935,16 +928,16 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |keyed_account| { + with_mockup(min_lamports + 42, true, |invoke_context, keyed_account| { let state = AccountUtilsState::::state(keyed_account) .unwrap() .convert_to_current(); assert_eq!(state, State::Uninitialized); let mut signers = HashSet::new(); signers.insert(*keyed_account.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(0); + set_invoke_context_blockhash(invoke_context, 0); let authority = *keyed_account.unsigned_key(); - let result = keyed_account.initialize_nonce_account(&authority, &rent, &invoke_context); + let result = keyed_account.initialize_nonce_account(&authority, &rent, invoke_context); let data = nonce::state::Data::new( authority, invoke_context.blockhash, @@ -965,15 +958,14 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |keyed_account| { - let invoke_context = create_invoke_context_with_blockhash(31); + with_mockup(min_lamports + 42, true, |invoke_context, keyed_account| { + set_invoke_context_blockhash(invoke_context, 31); let authorized = *keyed_account.unsigned_key(); keyed_account - .initialize_nonce_account(&authorized, &rent, &invoke_context) + .initialize_nonce_account(&authorized, &rent, invoke_context) .unwrap(); - let invoke_context = create_invoke_context_with_blockhash(0); - let result = - keyed_account.initialize_nonce_account(&authorized, &rent, &invoke_context); + set_invoke_context_blockhash(invoke_context, 0); + let result = keyed_account.initialize_nonce_account(&authorized, &rent, invoke_context); assert_eq!(result, Err(InstructionError::InvalidAccountData)); }) } @@ -985,11 +977,10 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports - 42, true, |keyed_account| { - let invoke_context = create_invoke_context_with_blockhash(63); + with_mockup(min_lamports - 42, true, |invoke_context, keyed_account| { + set_invoke_context_blockhash(invoke_context, 63); let authorized = *keyed_account.unsigned_key(); - let result = - keyed_account.initialize_nonce_account(&authorized, &rent, &invoke_context); + let result = keyed_account.initialize_nonce_account(&authorized, &rent, invoke_context); assert_eq!(result, Err(InstructionError::InsufficientFunds)); }) } @@ -1001,13 +992,13 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_account| { + with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| { let mut signers = HashSet::new(); signers.insert(*nonce_account.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(31); + set_invoke_context_blockhash(invoke_context, 31); let authorized = *nonce_account.unsigned_key(); nonce_account - .initialize_nonce_account(&authorized, &rent, &invoke_context) + .initialize_nonce_account(&authorized, &rent, invoke_context) .unwrap(); let authority = Pubkey::default(); let data = nonce::state::Data::new( @@ -1015,11 +1006,8 @@ mod test { invoke_context.blockhash, invoke_context.lamports_per_signature, ); - let result = nonce_account.authorize_nonce_account( - &Pubkey::default(), - &signers, - &invoke_context, - ); + let result = + nonce_account.authorize_nonce_account(&Pubkey::default(), &signers, invoke_context); assert_eq!(result, Ok(())); let state = AccountUtilsState::::state(nonce_account) .unwrap() @@ -1035,14 +1023,11 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_account| { + with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| { let mut signers = HashSet::new(); signers.insert(*nonce_account.signer_key().unwrap()); - let result = nonce_account.authorize_nonce_account( - &Pubkey::default(), - &signers, - &InvokeContext::new_mock(&[], &[]), - ); + let result = + nonce_account.authorize_nonce_account(&Pubkey::default(), &signers, invoke_context); assert_eq!(result, Err(InstructionError::InvalidAccountData)); }) } @@ -1054,35 +1039,32 @@ mod test { ..Rent::default() }; let min_lamports = rent.minimum_balance(State::size()); - with_test_keyed_account(min_lamports + 42, true, |nonce_account| { + with_mockup(min_lamports + 42, true, |invoke_context, nonce_account| { let mut signers = HashSet::new(); signers.insert(*nonce_account.signer_key().unwrap()); - let invoke_context = create_invoke_context_with_blockhash(31); + set_invoke_context_blockhash(invoke_context, 31); let authorized = &Pubkey::default().clone(); nonce_account - .initialize_nonce_account(authorized, &rent, &invoke_context) + .initialize_nonce_account(authorized, &rent, invoke_context) .unwrap(); - let result = nonce_account.authorize_nonce_account( - &Pubkey::default(), - &signers, - &invoke_context, - ); + let result = + nonce_account.authorize_nonce_account(&Pubkey::default(), &signers, invoke_context); assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); }) } #[test] fn verify_nonce_ok() { - with_test_keyed_account(42, true, |nonce_account| { + with_mockup(42, true, |invoke_context, nonce_account| { let mut signers = HashSet::new(); signers.insert(nonce_account.signer_key().unwrap()); let state: State = nonce_account.state().unwrap(); // New is in Uninitialzed state assert_eq!(state, State::Uninitialized); - let invoke_context = create_invoke_context_with_blockhash(0); + set_invoke_context_blockhash(invoke_context, 0); let authorized = nonce_account.unsigned_key(); nonce_account - .initialize_nonce_account(authorized, &Rent::free(), &invoke_context) + .initialize_nonce_account(authorized, &Rent::free(), invoke_context) .unwrap(); assert!(verify_nonce_account( &nonce_account.account.borrow(), @@ -1093,7 +1075,7 @@ mod test { #[test] fn verify_nonce_bad_acc_state_fail() { - with_test_keyed_account(42, true, |nonce_account| { + with_mockup(42, true, |_invoke_context, nonce_account| { assert!(!verify_nonce_account( &nonce_account.account.borrow(), &Hash::default() @@ -1103,18 +1085,18 @@ mod test { #[test] fn verify_nonce_bad_query_hash_fail() { - with_test_keyed_account(42, true, |nonce_account| { + with_mockup(42, true, |invoke_context, nonce_account| { let mut signers = HashSet::new(); signers.insert(nonce_account.signer_key().unwrap()); let state: State = nonce_account.state().unwrap(); // New is in Uninitialzed state assert_eq!(state, State::Uninitialized); - let invoke_context = create_invoke_context_with_blockhash(0); + set_invoke_context_blockhash(invoke_context, 0); let authorized = nonce_account.unsigned_key(); nonce_account - .initialize_nonce_account(authorized, &Rent::free(), &invoke_context) + .initialize_nonce_account(authorized, &Rent::free(), invoke_context) .unwrap(); - let invoke_context = create_invoke_context_with_blockhash(1); + set_invoke_context_blockhash(invoke_context, 1); assert!(!verify_nonce_account( &nonce_account.account.borrow(), &invoke_context.blockhash, diff --git a/runtime/src/system_instruction_processor.rs b/runtime/src/system_instruction_processor.rs index c1de986fd..b9639b35f 100644 --- a/runtime/src/system_instruction_processor.rs +++ b/runtime/src/system_instruction_processor.rs @@ -502,6 +502,7 @@ mod tests { system_instruction, system_program, sysvar, sysvar::recent_blockhashes::IterItem, transaction::TransactionError, + transaction_context::TransactionContext, }; use { super::*, @@ -674,7 +675,8 @@ mod tests { #[test] fn test_address_create_with_seed_mismatch() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let from = Pubkey::new_unique(); let seed = "dull boy"; let to = Pubkey::new_unique(); @@ -688,7 +690,8 @@ mod tests { #[test] fn test_create_account_with_seed_missing_sig() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); let seed = "dull boy"; @@ -718,7 +721,8 @@ mod tests { #[test] fn test_create_with_zero_lamports() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); // create account with zero lamports transferred let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); @@ -752,7 +756,8 @@ mod tests { #[test] fn test_create_negative_lamports() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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(); @@ -776,7 +781,8 @@ mod tests { #[test] fn test_request_more_than_allowed_data_length() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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())); @@ -823,7 +829,8 @@ mod tests { #[test] fn test_create_already_in_use() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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(); @@ -891,7 +898,8 @@ mod tests { #[test] fn test_create_unsigned() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); // Attempt to create an account without signing the transfer let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); @@ -946,7 +954,8 @@ mod tests { #[test] fn test_create_sysvar_invalid_id_with_feature() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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())); @@ -980,7 +989,8 @@ mod tests { feature_set .inactive .insert(feature_set::rent_for_sysvars::id()); - let mut invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let mut invoke_context = InvokeContext::new_mock(&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(); @@ -1007,7 +1017,8 @@ mod tests { #[test] fn test_create_data_populated() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); // Attempt to create system account in account with populated data let new_owner = Pubkey::new(&[9; 32]); let from = Pubkey::new_unique(); @@ -1040,7 +1051,8 @@ mod tests { #[test] fn test_create_from_account_is_nonce_fail() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let nonce = Pubkey::new_unique(); let nonce_account = RefCell::new( AccountSharedData::new_data( @@ -1078,7 +1090,8 @@ mod tests { #[test] fn test_assign() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let new_owner = Pubkey::new(&[9; 32]); let pubkey = Pubkey::new_unique(); let mut account = AccountSharedData::new(100, 0, &system_program::id()); @@ -1120,7 +1133,8 @@ mod tests { #[test] fn test_assign_to_sysvar_with_feature() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let new_owner = sysvar::id(); let from = Pubkey::new_unique(); let mut from_account = AccountSharedData::new(100, 0, &system_program::id()); @@ -1146,7 +1160,8 @@ mod tests { feature_set .inactive .insert(feature_set::rent_for_sysvars::id()); - let mut invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let mut invoke_context = InvokeContext::new_mock(&transaction_context, &[]); invoke_context.feature_set = Arc::new(feature_set); let new_owner = sysvar::id(); let from = Pubkey::new_unique(); @@ -1197,7 +1212,8 @@ mod tests { #[test] fn test_transfer_lamports() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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]); @@ -1235,7 +1251,8 @@ mod tests { #[test] fn test_transfer_with_seed() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&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); @@ -1295,7 +1312,8 @@ mod tests { #[test] fn test_transfer_lamports_from_nonce_account_fail() { - let invoke_context = InvokeContext::new_mock(&[], &[]); + let transaction_context = TransactionContext::new(Vec::new(), 1); + let invoke_context = InvokeContext::new_mock(&transaction_context, &[]); let from = Pubkey::new_unique(); let from_account = RefCell::new( AccountSharedData::new_data( diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 2df94726a..f537560b7 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -46,6 +46,7 @@ pub mod signer; pub mod system_transaction; pub mod timing; pub mod transaction; +pub mod transaction_context; pub mod transport; pub mod wasm; diff --git a/sdk/src/transaction_context.rs b/sdk/src/transaction_context.rs new file mode 100644 index 000000000..555a9226d --- /dev/null +++ b/sdk/src/transaction_context.rs @@ -0,0 +1,342 @@ +//! Successors of instruction_context_context::StackFrame, KeyedAccount and AccountInfo + +use crate::{ + account::{AccountSharedData, ReadableAccount, WritableAccount}, + instruction::InstructionError, + pubkey::Pubkey, +}; +use std::cell::{RefCell, RefMut}; + +pub type TransactionAccount = (Pubkey, AccountSharedData); + +#[derive(Clone, Debug)] +pub struct InstructionAccount { + pub index: usize, + pub is_signer: bool, + pub is_writable: bool, +} + +/// Loaded transaction shared between runtime and programs. +/// +/// This context is valid for the entire duration of a transaction being processed. +pub struct TransactionContext { + account_keys: Vec, + accounts: Vec>, + instruction_context_capacity: usize, + instruction_context_stack: Vec, + return_data: (Pubkey, Vec), +} + +impl TransactionContext { + /// Constructs a new TransactionContext + pub fn new( + transaction_accounts: Vec, + instruction_context_capacity: usize, + ) -> Self { + let (account_keys, accounts) = transaction_accounts + .into_iter() + .map(|(key, account)| (key, RefCell::new(account))) + .unzip(); + Self { + account_keys, + accounts, + instruction_context_capacity, + instruction_context_stack: Vec::with_capacity(instruction_context_capacity), + return_data: (Pubkey::default(), Vec::new()), + } + } + + /// Used by the bank in the runtime to write back the processed accounts + pub fn deconstruct(self) -> Vec { + self.account_keys + .into_iter() + .zip( + self.accounts + .into_iter() + .map(|account| account.into_inner()), + ) + .collect() + } + + /// Used in mock_process_instruction + pub fn deconstruct_without_keys(self) -> Result, InstructionError> { + if !self.instruction_context_stack.is_empty() { + return Err(InstructionError::CallDepth); + } + Ok(self + .accounts + .into_iter() + .map(|account| account.into_inner()) + .collect()) + } + + /// Returns the total number of accounts loaded in this Transaction + pub fn get_number_of_accounts(&self) -> usize { + self.accounts.len() + } + + /// Searches for an account by its key + pub fn get_key_of_account_at_index(&self, index_in_transaction: usize) -> &Pubkey { + &self.account_keys[index_in_transaction] + } + + /// Searches for an account by its key + pub fn get_account_at_index(&self, index_in_transaction: usize) -> &RefCell { + &self.accounts[index_in_transaction] + } + + /// Searches for an account by its key + pub fn find_index_of_account(&self, pubkey: &Pubkey) -> Option { + self.account_keys.iter().position(|key| key == pubkey) + } + + /// Searches for a program account by its key + pub fn find_index_of_program_account(&self, pubkey: &Pubkey) -> Option { + self.account_keys.iter().rposition(|key| key == pubkey) + } + + /// Gets an InstructionContext by its height in the stack + pub fn get_instruction_context_at( + &self, + instruction_context_height: usize, + ) -> Result<&InstructionContext, InstructionError> { + if instruction_context_height >= self.instruction_context_stack.len() { + return Err(InstructionError::CallDepth); + } + Ok(&self.instruction_context_stack[instruction_context_height]) + } + + /// Gets the max height of the InstructionContext stack + pub fn get_instruction_context_capacity(&self) -> usize { + self.instruction_context_capacity + } + + /// Gets the height of the current InstructionContext + pub fn get_instruction_context_height(&self) -> usize { + self.instruction_context_stack.len().saturating_sub(1) + } + + /// Returns the current InstructionContext + pub fn get_current_instruction_context(&self) -> Result<&InstructionContext, InstructionError> { + self.get_instruction_context_at(self.get_instruction_context_height()) + } + + /// Gets the last program account of the current InstructionContext + pub fn try_borrow_program_account(&self) -> Result { + let instruction_context = self.get_current_instruction_context()?; + instruction_context.try_borrow_account( + self, + instruction_context + .number_of_program_accounts + .saturating_sub(1), + ) + } + + /// Gets an instruction account of the current InstructionContext + pub fn try_borrow_instruction_account( + &self, + index_in_instruction: usize, + ) -> Result { + let instruction_context = self.get_current_instruction_context()?; + instruction_context.try_borrow_account( + self, + instruction_context + .number_of_program_accounts + .saturating_add(index_in_instruction), + ) + } + + /// Pushes a new InstructionContext + pub fn push( + &mut self, + number_of_program_accounts: usize, + instruction_accounts: &[InstructionAccount], + instruction_data: Vec, + ) -> Result<(), InstructionError> { + if self.instruction_context_stack.len() >= self.instruction_context_capacity { + return Err(InstructionError::CallDepth); + } + let mut result = InstructionContext { + instruction_data, + number_of_program_accounts, + account_indices: Vec::with_capacity(instruction_accounts.len()), + account_is_signer: Vec::with_capacity(instruction_accounts.len()), + account_is_writable: Vec::with_capacity(instruction_accounts.len()), + }; + for instruction_account in instruction_accounts.iter() { + result.account_indices.push(instruction_account.index); + result.account_is_signer.push(instruction_account.is_signer); + result + .account_is_writable + .push(instruction_account.is_writable); + } + self.instruction_context_stack.push(result); + Ok(()) + } + + /// Pops the current InstructionContext + pub fn pop(&mut self) -> Result<(), InstructionError> { + if self.instruction_context_stack.is_empty() { + return Err(InstructionError::CallDepth); + } + self.instruction_context_stack.pop(); + Ok(()) + } + + /// Gets the return data of the current InstructionContext or any above + pub fn get_return_data(&self) -> (&Pubkey, &[u8]) { + (&self.return_data.0, &self.return_data.1) + } + + /// Set the return data of the current InstructionContext + pub fn set_return_data(&mut self, data: Vec) -> Result<(), InstructionError> { + let pubkey = *self.try_borrow_program_account()?.get_key(); + self.return_data = (pubkey, data); + Ok(()) + } +} + +/// Loaded instruction shared between runtime and programs. +/// +/// This context is valid for the entire duration of a (possibly cross program) instruction being processed. +pub struct InstructionContext { + number_of_program_accounts: usize, + account_indices: Vec, + account_is_signer: Vec, + account_is_writable: Vec, + instruction_data: Vec, +} + +impl InstructionContext { + /// Number of program accounts + pub fn get_number_of_program_accounts(&self) -> usize { + self.number_of_program_accounts + } + + /// Number of accounts in this Instruction (without program accounts) + pub fn get_number_of_instruction_accounts(&self) -> usize { + self.account_indices + .len() + .saturating_sub(self.number_of_program_accounts) + } + + /// Total number of accounts in this Instruction (with program accounts) + pub fn get_total_number_of_accounts(&self) -> usize { + self.account_indices.len() + } + + /// Data parameter for the programs `process_instruction` handler + pub fn get_instruction_data(&self) -> &[u8] { + &self.instruction_data + } + + /// 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> { + if index_in_instruction >= self.account_indices.len() { + return Err(InstructionError::NotEnoughAccountKeys); + } + let index_in_transaction = self.account_indices[index_in_instruction]; + if index_in_transaction >= transaction_context.accounts.len() { + return Err(InstructionError::MissingAccount); + } + let account = transaction_context.accounts[index_in_transaction] + .try_borrow_mut() + .map_err(|_| InstructionError::AccountBorrowFailed)?; + Ok(BorrowedAccount { + transaction_context, + instruction_context: self, + index_in_transaction, + index_in_instruction, + account, + }) + } +} + +/// Shared account borrowed from the TransactionContext and an InstructionContext. +pub struct BorrowedAccount<'a> { + transaction_context: &'a TransactionContext, + instruction_context: &'a InstructionContext, + index_in_transaction: usize, + index_in_instruction: usize, + account: RefMut<'a, AccountSharedData>, +} + +impl<'a> BorrowedAccount<'a> { + /// Returns the public key of this account (transaction wide) + pub fn get_key(&self) -> &Pubkey { + &self.transaction_context.account_keys[self.index_in_transaction] + } + + /// Returns the owner of this account (transaction wide) + pub fn get_owner(&self) -> &Pubkey { + self.account.owner() + } + + /// 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); + } + self.account.set_owner(pubkey); + Ok(()) + } + + /// Returns the number of lamports of this account (transaction wide) + pub fn get_lamports(&self) -> u64 { + self.account.lamports() + } + + /// 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(()) + } + + /// Returns a read-only slice of the account data (transaction wide) + pub fn get_data(&self) -> &[u8] { + 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); + } + Ok(self.account.data_as_mut_slice()) + } + + /*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() + } + + /// 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(()) + } + + /// Returns whether this account is a signer (instruction wide) + pub fn is_signer(&self) -> bool { + self.instruction_context.account_is_signer[self.index_in_instruction] + } + + /// Returns whether this account is writable (instruction wide) + pub fn is_writable(&self) -> bool { + self.instruction_context.account_is_writable[self.index_in_instruction] + } +}