diff --git a/core/benches/banking_stage.rs b/core/benches/banking_stage.rs index 84a3b32ee..f4f122e97 100644 --- a/core/benches/banking_stage.rs +++ b/core/benches/banking_stage.rs @@ -302,7 +302,7 @@ fn simulate_process_entries( hash: next_hash(&bank.last_blockhash(), 1, &tx_vector), transactions: tx_vector, }; - process_entries(&bank, &[entry], randomize_txs, None, None).unwrap(); + process_entries(&bank, &mut [entry], randomize_txs, None, None).unwrap(); } #[allow(clippy::same_item_push)] diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index 6fde4ed2a..5a7ad8b06 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -754,7 +754,6 @@ impl BankingStage { if num_to_commit != 0 { let tx_results = bank.commit_transactions( txs, - None, &mut loaded_accounts, &results, tx_count, @@ -769,7 +768,6 @@ impl BankingStage { transaction_status_sender.send_transaction_status_batch( bank.clone(), batch.transactions(), - batch.iteration_order_vec(), tx_results.execution_results, TransactionBalancesSet::new(pre_balances, post_balances), TransactionTokenBalancesSet::new(pre_token_balances, post_token_balances), @@ -810,7 +808,7 @@ impl BankingStage { let mut lock_time = Measure::start("lock_time"); // Once accounts are locked, other threads cannot encode transactions that will modify the // same account state - let batch = bank.prepare_batch(txs, None); + let batch = bank.prepare_batch(txs); lock_time.stop(); let (result, mut retryable_txs) = Self::process_and_record_transactions_locked( @@ -1003,7 +1001,6 @@ impl BankingStage { }; let result = bank.check_transactions( transactions, - None, &filter, (MAX_PROCESSING_AGE) .saturating_sub(max_tx_fwd_delay) diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index 80bee083e..283ac0ce7 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -3103,7 +3103,7 @@ pub(crate) mod tests { let fail_tx = system_transaction::transfer(&mint_keypair, &keypair2.pubkey(), 2, Hash::default()); let entry_3 = next_entry(&entry_2.hash, 1, vec![fail_tx]); - let entries = vec![entry_1, entry_2, entry_3]; + let mut entries = vec![entry_1, entry_2, entry_3]; let shreds = entries_to_test_shreds(entries.clone(), slot, previous_slot, true, 0); blockstore.insert_shreds(shreds, None, false).unwrap(); @@ -3122,7 +3122,7 @@ pub(crate) mod tests { // that they are matched properly by get_rooted_block let _result = blockstore_processor::process_entries( &bank, - &entries, + &mut entries, true, Some(TransactionStatusSender { sender: transaction_status_sender, diff --git a/core/src/transaction_status_service.rs b/core/src/transaction_status_service.rs index babf80350..2cf3cbb53 100644 --- a/core/src/transaction_status_service.rs +++ b/core/src/transaction_status_service.rs @@ -4,9 +4,8 @@ use solana_ledger::{ blockstore::Blockstore, blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage}, }; -use solana_runtime::{ - bank::{Bank, InnerInstructionsList, NonceRollbackInfo, TransactionLogMessages}, - transaction_utils::OrderedIterator, +use solana_runtime::bank::{ + Bank, InnerInstructionsList, NonceRollbackInfo, TransactionLogMessages, }; use solana_transaction_status::{InnerInstructions, TransactionStatusMeta}; use std::{ @@ -58,7 +57,6 @@ impl TransactionStatusService { TransactionStatusMessage::Batch(TransactionStatusBatch { bank, transactions, - iteration_order, statuses, balances, token_balances, @@ -80,7 +78,7 @@ impl TransactionStatusService { Box::new(std::iter::repeat_with(Vec::new)) }; for ( - (_, transaction), + transaction, (status, nonce_rollback), pre_balances, post_balances, @@ -89,7 +87,7 @@ impl TransactionStatusService { inner_instructions, log_messages, ) in izip!( - OrderedIterator::new(&transactions, iteration_order.as_deref()), + &transactions, statuses, balances.pre_balances, balances.post_balances, diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index 074704825..cf3832f9a 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -25,7 +25,6 @@ use solana_runtime::{ bank_utils, commitment::VOTE_THRESHOLD_SIZE, transaction_batch::TransactionBatch, - transaction_utils::OrderedIterator, vote_account::ArcVoteAccount, vote_sender_types::ReplayVoteSender, }; @@ -76,10 +75,7 @@ fn get_first_error( fee_collection_results: Vec>, ) -> Option<(Result<()>, Signature)> { let mut first_err = None; - for (result, (_, transaction)) in fee_collection_results.iter().zip(OrderedIterator::new( - batch.transactions(), - batch.iteration_order(), - )) { + for (result, transaction) in fee_collection_results.iter().zip(batch.transactions()) { if let Err(ref err) = result { if first_err.is_none() { first_err = Some((result.clone(), transaction.signatures[0])); @@ -149,7 +145,6 @@ fn execute_batch( transaction_status_sender.send_transaction_status_batch( bank.clone(), batch.transactions(), - batch.iteration_order_vec(), execution_results, balances, token_balances, @@ -208,7 +203,7 @@ fn execute_batches( /// 4. Update the leader scheduler, goto 1 pub fn process_entries( bank: &Arc, - entries: &[Entry], + entries: &mut [Entry], randomize: bool, transaction_status_sender: Option, replay_vote_sender: Option<&ReplayVoteSender>, @@ -228,9 +223,10 @@ pub fn process_entries( result } +// Note: If randomize is true this will shuffle entries' transactions in-place. fn process_entries_with_callback( bank: &Arc, - entries: &[Entry], + entries: &mut [Entry], randomize: bool, entry_callback: Option<&ProcessCallback>, transaction_status_sender: Option, @@ -240,6 +236,12 @@ fn process_entries_with_callback( // accumulator for entries that can be processed in parallel let mut batches = vec![]; let mut tick_hashes = vec![]; + if randomize { + let mut rng = thread_rng(); + for entry in entries.iter_mut() { + entry.transactions.shuffle(&mut rng); + } + } for entry in entries { if entry.is_tick() { // If it's a tick, save it for later @@ -265,17 +267,8 @@ fn process_entries_with_callback( } // else loop on processing the entry loop { - let iteration_order = if randomize { - let mut iteration_order: Vec = (0..entry.transactions.len()).collect(); - iteration_order.shuffle(&mut thread_rng()); - Some(iteration_order) - } else { - None - }; - // try to lock the accounts - let batch = bank.prepare_batch(&entry.transactions, iteration_order); - + let batch = bank.prepare_batch(&entry.transactions); let first_lock_err = first_err(batch.lock_results()); // if locking worked @@ -677,7 +670,7 @@ pub fn confirm_slot( ) -> result::Result<(), BlockstoreProcessorError> { let slot = bank.slot(); - let (entries, num_shreds, slot_full) = { + let (mut entries, num_shreds, slot_full) = { let mut load_elapsed = Measure::start("load_elapsed"); let load_result = blockstore .get_slot_entries_with_shred_info(slot, progress.num_shreds, allow_dead_slots) @@ -738,10 +731,11 @@ pub fn confirm_slot( let mut replay_elapsed = Measure::start("replay_elapsed"); let mut execute_timings = ExecuteTimings::default(); + // Note: This will shuffle entries' transactions in-place. let process_result = process_entries_with_callback( bank, - &entries, - true, + &mut entries, + true, // shuffle transactions. entry_callback, transaction_status_sender, replay_vote_sender, @@ -1113,7 +1107,6 @@ pub enum TransactionStatusMessage { pub struct TransactionStatusBatch { pub bank: Arc, pub transactions: Vec, - pub iteration_order: Option>, pub statuses: Vec, pub balances: TransactionBalancesSet, pub token_balances: TransactionTokenBalancesSet, @@ -1132,7 +1125,6 @@ impl TransactionStatusSender { &self, bank: Arc, transactions: &[Transaction], - iteration_order: Option>, statuses: Vec, balances: TransactionBalancesSet, token_balances: TransactionTokenBalancesSet, @@ -1150,7 +1142,6 @@ impl TransactionStatusSender { .send(TransactionStatusMessage::Batch(TransactionStatusBatch { bank, transactions: transactions.to_vec(), - iteration_order, statuses, balances, token_balances, @@ -1897,7 +1888,8 @@ pub mod tests { } = create_genesis_config(2); let bank = Arc::new(Bank::new(&genesis_config)); let keypair = Keypair::new(); - let slot_entries = create_ticks(genesis_config.ticks_per_slot, 1, genesis_config.hash()); + let mut slot_entries = + create_ticks(genesis_config.ticks_per_slot, 1, genesis_config.hash()); let tx = system_transaction::transfer( &mint_keypair, &keypair.pubkey(), @@ -1912,7 +1904,7 @@ pub mod tests { ); // Now ensure the TX is accepted despite pointing to the ID of an empty entry. - process_entries(&bank, &slot_entries, true, None, None).unwrap(); + process_entries(&bank, &mut slot_entries, true, None, None).unwrap(); assert_eq!(bank.process_transaction(&tx), Ok(())); } @@ -2117,7 +2109,10 @@ pub mod tests { // ensure bank can process a tick assert_eq!(bank.tick_height(), 0); let tick = next_entry(&genesis_config.hash(), 1, vec![]); - assert_eq!(process_entries(&bank, &[tick], true, None, None), Ok(())); + assert_eq!( + process_entries(&bank, &mut [tick], true, None, None), + Ok(()) + ); assert_eq!(bank.tick_height(), 1); } @@ -2150,7 +2145,7 @@ pub mod tests { ); let entry_2 = next_entry(&entry_1.hash, 1, vec![tx]); assert_eq!( - process_entries(&bank, &[entry_1, entry_2], true, None, None), + process_entries(&bank, &mut [entry_1, entry_2], true, None, None), Ok(()) ); assert_eq!(bank.get_balance(&keypair1.pubkey()), 2); @@ -2208,7 +2203,7 @@ pub mod tests { assert_eq!( process_entries( &bank, - &[entry_1_to_mint, entry_2_to_3_mint_to_1], + &mut [entry_1_to_mint, entry_2_to_3_mint_to_1], false, None, None, @@ -2280,7 +2275,7 @@ pub mod tests { assert!(process_entries( &bank, - &[entry_1_to_mint.clone(), entry_2_to_3_mint_to_1.clone()], + &mut [entry_1_to_mint.clone(), entry_2_to_3_mint_to_1.clone()], false, None, None, @@ -2294,13 +2289,13 @@ pub mod tests { // Check all accounts are unlocked let txs1 = &entry_1_to_mint.transactions[..]; let txs2 = &entry_2_to_3_mint_to_1.transactions[..]; - let batch1 = bank.prepare_batch(txs1, None); + let batch1 = bank.prepare_batch(txs1); for result in batch1.lock_results() { assert!(result.is_ok()); } // txs1 and txs2 have accounts that conflict, so we must drop txs1 first drop(batch1); - let batch2 = bank.prepare_batch(txs2, None); + let batch2 = bank.prepare_batch(txs2); for result in batch2.lock_results() { assert!(result.is_ok()); } @@ -2388,7 +2383,7 @@ pub mod tests { assert!(process_entries( &bank, - &[ + &mut [ entry_1_to_mint, entry_2_to_3_and_1_to_mint, entry_conflict_itself, @@ -2443,7 +2438,7 @@ pub mod tests { system_transaction::transfer(&keypair2, &keypair4.pubkey(), 1, bank.last_blockhash()); let entry_2 = next_entry(&entry_1.hash, 1, vec![tx]); assert_eq!( - process_entries(&bank, &[entry_1, entry_2], true, None, None), + process_entries(&bank, &mut [entry_1, entry_2], true, None, None), Ok(()) ); assert_eq!(bank.get_balance(&keypair3.pubkey()), 1); @@ -2477,7 +2472,7 @@ pub mod tests { let present_account = AccountSharedData::new(1, 10, &Pubkey::default()); bank.store_account(&present_account_key.pubkey(), &present_account); - let entries: Vec<_> = (0..NUM_TRANSFERS) + let mut entries: Vec<_> = (0..NUM_TRANSFERS) .step_by(NUM_TRANSFERS_PER_ENTRY) .map(|i| { let mut transactions = (0..NUM_TRANSFERS_PER_ENTRY) @@ -2503,7 +2498,10 @@ pub mod tests { next_entry_mut(&mut hash, 0, transactions) }) .collect(); - assert_eq!(process_entries(&bank, &entries, true, None, None), Ok(())); + assert_eq!( + process_entries(&bank, &mut entries, true, None, None), + Ok(()) + ); } #[test] @@ -2563,7 +2561,10 @@ pub mod tests { // Transfer lamports to each other let entry = next_entry(&bank.last_blockhash(), 1, tx_vector); - assert_eq!(process_entries(&bank, &[entry], true, None, None), Ok(())); + assert_eq!( + process_entries(&bank, &mut [entry], true, None, None), + Ok(()) + ); bank.squash(); // Even number keypair should have balance of 2 * initial_lamports and @@ -2621,7 +2622,13 @@ pub mod tests { system_transaction::transfer(&keypair1, &keypair4.pubkey(), 1, bank.last_blockhash()); let entry_2 = next_entry(&tick.hash, 1, vec![tx]); assert_eq!( - process_entries(&bank, &[entry_1, tick, entry_2.clone()], true, None, None), + process_entries( + &bank, + &mut [entry_1, tick, entry_2.clone()], + true, + None, + None + ), Ok(()) ); assert_eq!(bank.get_balance(&keypair3.pubkey()), 1); @@ -2632,7 +2639,7 @@ pub mod tests { system_transaction::transfer(&keypair2, &keypair3.pubkey(), 1, bank.last_blockhash()); let entry_3 = next_entry(&entry_2.hash, 1, vec![tx]); assert_eq!( - process_entries(&bank, &[entry_3], true, None, None), + process_entries(&bank, &mut [entry_3], true, None, None), Err(TransactionError::AccountNotFound) ); } @@ -2712,7 +2719,7 @@ pub mod tests { ); assert_eq!( - process_entries(&bank, &[entry_1_to_mint], false, None, None), + process_entries(&bank, &mut [entry_1_to_mint], false, None, None), Err(TransactionError::AccountInUse) ); @@ -2863,7 +2870,7 @@ pub mod tests { let mut hash = bank.last_blockhash(); let mut root: Option> = None; loop { - let entries: Vec<_> = (0..NUM_TRANSFERS) + let mut entries: Vec<_> = (0..NUM_TRANSFERS) .step_by(NUM_TRANSFERS_PER_ENTRY) .map(|i| { next_entry_mut(&mut hash, 0, { @@ -2891,9 +2898,9 @@ pub mod tests { }) .collect(); info!("paying iteration {}", i); - process_entries(&bank, &entries, true, None, None).expect("paying failed"); + process_entries(&bank, &mut entries, true, None, None).expect("paying failed"); - let entries: Vec<_> = (0..NUM_TRANSFERS) + let mut entries: Vec<_> = (0..NUM_TRANSFERS) .step_by(NUM_TRANSFERS_PER_ENTRY) .map(|i| { next_entry_mut( @@ -2914,12 +2921,12 @@ pub mod tests { .collect(); info!("refunding iteration {}", i); - process_entries(&bank, &entries, true, None, None).expect("refunding failed"); + process_entries(&bank, &mut entries, true, None, None).expect("refunding failed"); // advance to next block process_entries( &bank, - &(0..bank.ticks_per_slot()) + &mut (0..bank.ticks_per_slot()) .map(|_| next_entry_mut(&mut hash, 1, vec![])) .collect::>(), true, @@ -2969,7 +2976,7 @@ pub mod tests { process_entries_with_callback( &bank0, - &entries, + &mut entries, true, None, None, @@ -3049,12 +3056,8 @@ pub mod tests { bank.last_blockhash(), ); account_loaded_twice.message.account_keys[1] = mint_keypair.pubkey(); - let transactions = [account_loaded_twice, account_not_found_tx]; - - // Use inverted iteration_order - let iteration_order: Vec = vec![1, 0]; - - let batch = bank.prepare_batch(&transactions, Some(iteration_order)); + let transactions = [account_not_found_tx, account_loaded_twice]; + let batch = bank.prepare_batch(&transactions); let ( TransactionResults { fee_collection_results, @@ -3150,7 +3153,7 @@ pub mod tests { .collect(); let entry = next_entry(&bank_1_blockhash, 1, vote_txs); let (replay_vote_sender, replay_vote_receiver) = unbounded(); - let _ = process_entries(&bank1, &[entry], true, None, Some(&replay_vote_sender)); + let _ = process_entries(&bank1, &mut [entry], true, None, Some(&replay_vote_sender)); let successes: BTreeSet = replay_vote_receiver .try_iter() .map(|(vote_pubkey, _, _)| vote_pubkey) diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index cf3ba46c8..0522a5dab 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -375,12 +375,11 @@ fn setup_fee_calculator(bank: Bank) -> Bank { // initialized with a non-zero fee. assert_eq!(bank.signature_count(), 0); bank.commit_transactions( - &[], - None, - &mut [], - &[], - 0, - 1, + &[], // transactions + &mut [], // loaded accounts + &[], // transaction execution results + 0, // tx count + 1, // signature count &mut ExecuteTimings::default(), ); assert_eq!(bank.signature_count(), 1); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 87f1dabfa..2d291721a 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -269,7 +269,7 @@ fn process_transaction_and_record_inner( ) -> (Result<(), TransactionError>, Vec>) { let signature = tx.signatures.get(0).unwrap().clone(); let txs = vec![tx]; - let tx_batch = bank.prepare_batch(&txs, None); + let tx_batch = bank.prepare_batch(&txs); let (mut results, _, mut inner, _transaction_logs) = bank.load_execute_and_commit_transactions( &tx_batch, MAX_PROCESSING_AGE, @@ -294,7 +294,7 @@ fn process_transaction_and_record_inner( } fn execute_transactions(bank: &Bank, txs: &[Transaction]) -> Vec { - let batch = bank.prepare_batch(txs, None); + let batch = bank.prepare_batch(txs); let mut timings = ExecuteTimings::default(); let mut mint_decimals = HashMap::new(); let tx_pre_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals); diff --git a/runtime/benches/transaction_utils.rs b/runtime/benches/transaction_utils.rs deleted file mode 100644 index 7c45718fa..000000000 --- a/runtime/benches/transaction_utils.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![feature(test)] - -extern crate test; - -use rand::{seq::SliceRandom, thread_rng}; -use solana_runtime::transaction_utils::OrderedIterator; -use test::Bencher; - -#[bench] -fn bench_ordered_iterator_with_order_shuffling(bencher: &mut Bencher) { - let vec: Vec = (0..100_usize).collect(); - bencher.iter(|| { - let mut order: Vec = (0..100_usize).collect(); - order.shuffle(&mut thread_rng()); - let _ordered_iterator_resp: Vec<(usize, &usize)> = - OrderedIterator::new(&vec, Some(&order)).collect(); - }); -} diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 9bc03db43..4b3e6308b 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -7,7 +7,6 @@ use crate::{ blockhash_queue::BlockhashQueue, rent_collector::RentCollector, system_instruction_processor::{get_system_account_kind, SystemAccountKind}, - transaction_utils::OrderedIterator, }; use dashmap::{ mapref::entry::Entry::{Occupied, Vacant}, @@ -391,7 +390,6 @@ impl Accounts { &self, ancestors: &Ancestors, txs: &[Transaction], - txs_iteration_order: Option<&[usize]>, lock_results: Vec, hash_queue: &BlockhashQueue, error_counters: &mut ErrorCounters, @@ -402,10 +400,10 @@ impl Accounts { secp256k1_program_enabled: feature_set .is_active(&feature_set::secp256k1_program_enabled::id()), }; - OrderedIterator::new(txs, txs_iteration_order) - .zip(lock_results.into_iter()) + txs.iter() + .zip(lock_results) .map(|etx| match etx { - ((_, tx), (Ok(()), nonce_rollback)) => { + (tx, (Ok(()), nonce_rollback)) => { let fee_calculator = nonce_rollback .as_ref() .map(|nonce_rollback| nonce_rollback.fee_calculator()) @@ -804,12 +802,12 @@ impl Accounts { pub fn lock_accounts( &self, txs: &[Transaction], - txs_iteration_order: Option<&[usize]>, demote_sysvar_write_locks: bool, ) -> Vec> { use solana_sdk::sanitize::Sanitize; - let keys: Vec> = OrderedIterator::new(txs, txs_iteration_order) - .map(|(_, tx)| { + let keys: Vec> = txs + .iter() + .map(|tx| { tx.sanitize().map_err(TransactionError::from)?; if Self::has_duplicates(&tx.message.account_keys) { @@ -836,18 +834,19 @@ impl Accounts { pub fn unlock_accounts( &self, txs: &[Transaction], - txs_iteration_order: Option<&[usize]>, results: &[Result<()>], demote_sysvar_write_locks: bool, ) { let mut account_locks = self.account_locks.lock().unwrap(); debug!("bank unlock accounts"); - - OrderedIterator::new(txs, txs_iteration_order) - .zip(results.iter()) - .for_each(|((_, tx), result)| { - self.unlock_account(tx, result, &mut account_locks, demote_sysvar_write_locks) - }); + for (tx, lock_result) in txs.iter().zip(results) { + self.unlock_account( + tx, + lock_result, + &mut account_locks, + demote_sysvar_write_locks, + ); + } } /// Store the accounts into the DB @@ -857,7 +856,6 @@ impl Accounts { &self, slot: Slot, txs: &[Transaction], - txs_iteration_order: Option<&[usize]>, res: &[TransactionExecutionResult], loaded: &mut [TransactionLoadResult], rent_collector: &RentCollector, @@ -867,7 +865,6 @@ impl Accounts { ) { let accounts_to_store = self.collect_accounts_to_store( txs, - txs_iteration_order, res, loaded, rent_collector, @@ -892,7 +889,6 @@ impl Accounts { fn collect_accounts_to_store<'a>( &self, txs: &'a [Transaction], - txs_iteration_order: Option<&'a [usize]>, res: &'a [TransactionExecutionResult], loaded: &'a mut [TransactionLoadResult], rent_collector: &RentCollector, @@ -901,11 +897,7 @@ impl Accounts { demote_sysvar_write_locks: bool, ) -> Vec<(&'a Pubkey, &'a AccountSharedData)> { let mut accounts = Vec::with_capacity(loaded.len()); - for (i, ((raccs, _nonce_rollback), (_, tx))) in loaded - .iter_mut() - .zip(OrderedIterator::new(txs, txs_iteration_order)) - .enumerate() - { + for (i, ((raccs, _nonce_rollback), tx)) in loaded.iter_mut().zip(txs).enumerate() { if raccs.is_err() { continue; } @@ -1086,7 +1078,6 @@ mod tests { accounts.load_accounts( &ancestors, &[tx], - None, vec![(Ok(()), None)], &hash_queue, error_counters, @@ -1689,7 +1680,6 @@ mod tests { let tx = Transaction::new(&[&keypair0], message, Hash::default()); let results0 = accounts.lock_accounts( &[tx.clone()], - None, // txs_iteration_order true, // demote_sysvar_write_locks ); @@ -1727,8 +1717,7 @@ mod tests { let tx1 = Transaction::new(&[&keypair1], message, Hash::default()); let txs = vec![tx0, tx1]; let results1 = accounts.lock_accounts( - &txs, None, // txs_iteration_order - true, // demote_sysvar_write_locks + &txs, true, // demote_sysvar_write_locks ); assert!(results1[0].is_ok()); // Read-only account (keypair1) can be referenced multiple times @@ -1746,13 +1735,11 @@ mod tests { accounts.unlock_accounts( &[tx], - None, // txs_iteration_order &results0, true, // demote_sysvar_write_locks ); accounts.unlock_accounts( - &txs, None, // txs_iteration_order - &results1, true, // demote_sysvar_write_locks + &txs, &results1, true, // demote_sysvar_write_locks ); let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])]; let message = Message::new_with_compiled_instructions( @@ -1766,7 +1753,6 @@ mod tests { let tx = Transaction::new(&[&keypair1], message, Hash::default()); let results2 = accounts.lock_accounts( &[tx], - None, // txs_iteration_order true, // demote_sysvar_write_locks ); assert!(results2[0].is_ok()); // Now keypair1 account can be locked as writable @@ -1833,8 +1819,7 @@ mod tests { loop { let txs = vec![writable_tx.clone()]; let results = accounts_clone.clone().lock_accounts( - &txs, None, // txs_iteration_order - true, // demote_sysvar_write_locks + &txs, true, // demote_sysvar_write_locks ); for result in results.iter() { if result.is_ok() { @@ -1842,8 +1827,7 @@ mod tests { } } accounts_clone.unlock_accounts( - &txs, None, // txs_iteration_order - &results, true, // demote_sysvar_write_locks + &txs, &results, true, // demote_sysvar_write_locks ); if exit_clone.clone().load(Ordering::Relaxed) { break; @@ -1854,8 +1838,7 @@ mod tests { for _ in 0..5 { let txs = vec![readonly_tx.clone()]; let results = accounts_arc.clone().lock_accounts( - &txs, None, // txs_iteration_order - true, // demote_sysvar_write_locks + &txs, true, // demote_sysvar_write_locks ); if results[0].is_ok() { let counter_value = counter_clone.clone().load(Ordering::SeqCst); @@ -1863,8 +1846,7 @@ mod tests { assert_eq!(counter_value, counter_clone.clone().load(Ordering::SeqCst)); } accounts_arc.unlock_accounts( - &txs, None, // txs_iteration_order - &results, true, // demote_sysvar_write_locks + &txs, &results, true, // demote_sysvar_write_locks ); thread::sleep(time::Duration::from_millis(50)); } @@ -1947,7 +1929,6 @@ mod tests { } let collected_accounts = accounts.collect_accounts_to_store( &txs, - None, &loaders, loaded.as_mut_slice(), &rent_collector, @@ -2017,7 +1998,6 @@ mod tests { accounts.load_accounts( &ancestors, &[tx], - None, vec![(Ok(()), None)], &hash_queue, &mut error_counters, @@ -2313,7 +2293,6 @@ mod tests { Accounts::new_with_config(Vec::new(), &ClusterType::Development, HashSet::new(), false); let collected_accounts = accounts.collect_accounts_to_store( &txs, - None, &loaders, loaded.as_mut_slice(), &rent_collector, @@ -2425,7 +2404,6 @@ mod tests { Accounts::new_with_config(Vec::new(), &ClusterType::Development, HashSet::new(), false); let collected_accounts = accounts.collect_accounts_to_store( &txs, - None, &loaders, loaded.as_mut_slice(), &rent_collector, diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 19a8d2b0c..54048c149 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -21,7 +21,6 @@ use crate::{ status_cache::{SlotDelta, StatusCache}, system_instruction_processor::{get_system_account_kind, SystemAccountKind}, transaction_batch::TransactionBatch, - transaction_utils::OrderedIterator, vote_account::ArcVoteAccount, }; use byteorder::{ByteOrder, LittleEndian}; @@ -2347,15 +2346,10 @@ impl Bank { } } - fn update_transaction_statuses( - &self, - txs: &[Transaction], - iteration_order: Option<&[usize]>, - res: &[TransactionExecutionResult], - ) { + fn update_transaction_statuses(&self, txs: &[Transaction], res: &[TransactionExecutionResult]) { let mut status_cache = self.src.status_cache.write().unwrap(); - for (i, (_, tx)) in OrderedIterator::new(txs, iteration_order).enumerate() { - let (res, _nonce_rollback) = &res[i]; + assert_eq!(txs.len(), res.len()); + for (tx, (res, _nonce_rollback)) in txs.iter().zip(res) { if Self::can_commit(res) && !tx.signatures.is_empty() { status_cache.insert( &tx.message().recent_blockhash, @@ -2416,17 +2410,12 @@ impl Bank { .is_active(&feature_set::demote_sysvar_write_locks::id()) } - pub fn prepare_batch<'a, 'b>( - &'a self, - txs: &'b [Transaction], - iteration_order: Option>, - ) -> TransactionBatch<'a, 'b> { - let results = self.rc.accounts.lock_accounts( - txs, - iteration_order.as_deref(), - self.demote_sysvar_write_locks(), - ); - TransactionBatch::new(results, &self, txs, iteration_order) + pub fn prepare_batch<'a, 'b>(&'a self, txs: &'b [Transaction]) -> TransactionBatch<'a, 'b> { + let lock_results = self + .rc + .accounts + .lock_accounts(txs, self.demote_sysvar_write_locks()); + TransactionBatch::new(lock_results, &self, txs) } pub fn prepare_simulation_batch<'a, 'b>( @@ -2437,7 +2426,7 @@ impl Bank { .iter() .map(|tx| tx.sanitize().map_err(|e| e.into())) .collect(); - let mut batch = TransactionBatch::new(lock_results, &self, txs, None); + let mut batch = TransactionBatch::new(lock_results, &self, txs); batch.needs_unlock = false; batch } @@ -2488,7 +2477,6 @@ impl Bank { batch.needs_unlock = false; self.rc.accounts.unlock_accounts( batch.transactions(), - batch.iteration_order(), batch.lock_results(), self.demote_sysvar_write_locks(), ) @@ -2506,15 +2494,14 @@ impl Bank { fn check_age( &self, txs: &[Transaction], - iteration_order: Option<&[usize]>, lock_results: Vec>, max_age: usize, error_counters: &mut ErrorCounters, ) -> Vec { let hash_queue = self.blockhash_queue.read().unwrap(); - OrderedIterator::new(txs, iteration_order) - .zip(lock_results.into_iter()) - .map(|((_, tx), lock_res)| match lock_res { + txs.iter() + .zip(lock_results) + .map(|(tx, lock_res)| match lock_res { Ok(()) => { let message = tx.message(); let hash_age = hash_queue.check_hash_age(&message.recent_blockhash, max_age); @@ -2538,14 +2525,13 @@ impl Bank { fn check_signatures( &self, txs: &[Transaction], - iteration_order: Option<&[usize]>, lock_results: Vec, error_counters: &mut ErrorCounters, ) -> Vec { let rcache = self.src.status_cache.read().unwrap(); - OrderedIterator::new(txs, iteration_order) - .zip(lock_results.into_iter()) - .map(|((_, tx), lock_res)| { + txs.iter() + .zip(lock_results) + .map(|(tx, lock_res)| { if tx.signatures.is_empty() { return lock_res; } @@ -2572,13 +2558,12 @@ impl Bank { fn filter_by_vote_transactions( &self, txs: &[Transaction], - iteration_order: Option<&[usize]>, lock_results: Vec, error_counters: &mut ErrorCounters, ) -> Vec { - OrderedIterator::new(txs, iteration_order) - .zip(lock_results.into_iter()) - .map(|((_, tx), lock_res)| { + txs.iter() + .zip(lock_results) + .map(|(tx, lock_res)| { if lock_res.0.is_ok() { if is_simple_vote_transaction(tx) { return lock_res; @@ -2627,28 +2612,15 @@ impl Bank { pub fn check_transactions( &self, txs: &[Transaction], - iteration_order: Option<&[usize]>, lock_results: &[Result<()>], max_age: usize, mut error_counters: &mut ErrorCounters, ) -> Vec { - let age_results = self.check_age( - txs, - iteration_order, - lock_results.to_vec(), - max_age, - &mut error_counters, - ); - let sigcheck_results = - self.check_signatures(txs, iteration_order, age_results, &mut error_counters); + let age_results = self.check_age(txs, lock_results.to_vec(), max_age, &mut error_counters); + let sigcheck_results = self.check_signatures(txs, age_results, &mut error_counters); if self.upgrade_epoch() { // Reject all non-vote transactions - self.filter_by_vote_transactions( - txs, - iteration_order, - sigcheck_results, - &mut error_counters, - ) + self.filter_by_vote_transactions(txs, sigcheck_results, &mut error_counters) } else { sigcheck_results } @@ -2656,8 +2628,7 @@ impl Bank { pub fn collect_balances(&self, batch: &TransactionBatch) -> TransactionBalances { let mut balances: TransactionBalances = vec![]; - for (_, transaction) in OrderedIterator::new(batch.transactions(), batch.iteration_order()) - { + for transaction in batch.transactions() { let mut transaction_balances: Vec = vec![]; for account_key in transaction.message.account_keys.iter() { transaction_balances.push(self.get_balance(account_key)); @@ -2882,30 +2853,25 @@ impl Bank { let mut error_counters = ErrorCounters::default(); let mut load_time = Measure::start("accounts_load"); - let retryable_txs: Vec<_> = - OrderedIterator::new(batch.lock_results(), batch.iteration_order()) - .enumerate() - .filter_map(|(index, (_, res))| match res { - Err(TransactionError::AccountInUse) => { - error_counters.account_in_use += 1; - Some(index) - } - Ok(_) => None, - Err(_) => None, - }) - .collect(); + let retryable_txs: Vec<_> = batch + .lock_results() + .iter() + .enumerate() + .filter_map(|(index, res)| match res { + Err(TransactionError::AccountInUse) => { + error_counters.account_in_use += 1; + Some(index) + } + Err(_) => None, + Ok(_) => None, + }) + .collect(); - let sig_results = self.check_transactions( - txs, - batch.iteration_order(), - batch.lock_results(), - max_age, - &mut error_counters, - ); + let sig_results = + self.check_transactions(txs, batch.lock_results(), max_age, &mut error_counters); let mut loaded_accounts = self.rc.accounts.load_accounts( &self.ancestors, txs, - batch.iteration_order(), sig_results, &self.blockhash_queue.read().unwrap(), &mut error_counters, @@ -2925,8 +2891,8 @@ impl Bank { let executed: Vec = loaded_accounts .iter_mut() - .zip(OrderedIterator::new(txs, batch.iteration_order())) - .map(|(accs, (_, tx))| match accs { + .zip(txs) + .map(|(accs, tx)| match accs { (Err(e), _nonce_rollback) => (Err(e.clone()), None), (Ok(loaded_transaction), nonce_rollback) => { signature_count += u64::from(tx.message().header.num_required_signatures); @@ -3025,11 +2991,7 @@ impl Bank { let transaction_log_collector_config = self.transaction_log_collector_config.read().unwrap(); - for (i, ((r, _nonce_rollback), (_, tx))) in executed - .iter() - .zip(OrderedIterator::new(txs, batch.iteration_order())) - .enumerate() - { + for (i, ((r, _nonce_rollback), tx)) in executed.iter().zip(txs).enumerate() { if let Some(debug_keys) = &self.transaction_debug_keys { for key in &tx.message.account_keys { if debug_keys.contains(key) { @@ -3112,7 +3074,6 @@ impl Bank { fn filter_program_errors_and_collect_fee( &self, txs: &[Transaction], - iteration_order: Option<&[usize]>, executed: &[TransactionExecutionResult], ) -> Vec> { let hash_queue = self.blockhash_queue.read().unwrap(); @@ -3122,9 +3083,10 @@ impl Bank { secp256k1_program_enabled: self.secp256k1_program_enabled(), }; - let results = OrderedIterator::new(txs, iteration_order) - .zip(executed.iter()) - .map(|((_, tx), (res, nonce_rollback))| { + let results = txs + .iter() + .zip(executed) + .map(|(tx, (res, nonce_rollback))| { let (fee_calculator, is_durable_nonce) = nonce_rollback .as_ref() .map(|nonce_rollback| nonce_rollback.fee_calculator()) @@ -3172,7 +3134,6 @@ impl Bank { pub fn commit_transactions( &self, txs: &[Transaction], - iteration_order: Option<&[usize]>, loaded_accounts: &mut [TransactionLoadResult], executed: &[TransactionExecutionResult], tx_count: u64, @@ -3211,7 +3172,6 @@ impl Bank { self.rc.accounts.store_cached( self.slot(), txs, - iteration_order, executed, loaded_accounts, &self.rent_collector, @@ -3221,16 +3181,14 @@ impl Bank { ); self.collect_rent(executed, loaded_accounts); - let overwritten_vote_accounts = - self.update_cached_accounts(txs, iteration_order, executed, loaded_accounts); + let overwritten_vote_accounts = self.update_cached_accounts(txs, executed, loaded_accounts); // once committed there is no way to unroll write_time.stop(); debug!("store: {}us txs_len={}", write_time.as_us(), txs.len(),); timings.store_us += write_time.as_us(); - self.update_transaction_statuses(txs, iteration_order, &executed); - let fee_collection_results = - self.filter_program_errors_and_collect_fee(txs, iteration_order, executed); + self.update_transaction_statuses(txs, &executed); + let fee_collection_results = self.filter_program_errors_and_collect_fee(txs, executed); TransactionResults { fee_collection_results, @@ -3830,7 +3788,6 @@ impl Bank { let results = self.commit_transactions( batch.transactions(), - batch.iteration_order(), &mut loaded_accounts, &executed, tx_count, @@ -3852,7 +3809,7 @@ impl Bank { #[must_use] pub fn process_transactions(&self, txs: &[Transaction]) -> Vec> { - let batch = self.prepare_batch(txs, None); + let batch = self.prepare_batch(txs); self.load_execute_and_commit_transactions( &batch, MAX_PROCESSING_AGE, @@ -4421,16 +4378,11 @@ impl Bank { fn update_cached_accounts( &self, txs: &[Transaction], - iteration_order: Option<&[usize]>, res: &[TransactionExecutionResult], loaded: &[TransactionLoadResult], ) -> Vec { let mut overwritten_vote_accounts = vec![]; - for (i, ((raccs, _load_nonce_rollback), (transaction_index, tx))) in loaded - .iter() - .zip(OrderedIterator::new(txs, iteration_order)) - .enumerate() - { + for (i, ((raccs, _load_nonce_rollback), tx)) in loaded.iter().zip(txs).enumerate() { let (res, _res_nonce_rollback) = &res[i]; if res.is_err() || raccs.is_err() { continue; @@ -4452,9 +4404,10 @@ impl Bank { self.stake_program_v2_enabled(), self.check_init_vote_data_enabled(), ) { + // TODO: one of the indices is redundant. overwritten_vote_accounts.push(OverwrittenVoteAccount { account: old_vote_account, - transaction_index, + transaction_index: i, transaction_result_index: i, }); } @@ -7600,7 +7553,7 @@ pub(crate) mod tests { ]; let initial_balance = bank.get_balance(&leader); - let results = bank.filter_program_errors_and_collect_fee(&[tx1, tx2], None, &results); + let results = bank.filter_program_errors_and_collect_fee(&[tx1, tx2], &results); bank.freeze(); assert_eq!( bank.get_balance(&leader), @@ -7726,7 +7679,7 @@ pub(crate) mod tests { system_transaction::transfer(&mint_keypair, &alice.pubkey(), 1, genesis_config.hash()); let pay_alice = vec![tx1]; - let lock_result = bank.prepare_batch(&pay_alice, None); + let lock_result = bank.prepare_batch(&pay_alice); let results_alice = bank .load_execute_and_commit_transactions( &lock_result, @@ -7779,7 +7732,7 @@ pub(crate) mod tests { let tx = Transaction::new(&[&key0], message, genesis_config.hash()); let txs = vec![tx]; - let batch0 = bank.prepare_batch(&txs, None); + let batch0 = bank.prepare_batch(&txs); assert!(batch0.lock_results()[0].is_ok()); // Try locking accounts, locking a previously read-only account as writable @@ -7797,7 +7750,7 @@ pub(crate) mod tests { let tx = Transaction::new(&[&key1], message, genesis_config.hash()); let txs = vec![tx]; - let batch1 = bank.prepare_batch(&txs, None); + let batch1 = bank.prepare_batch(&txs); assert!(batch1.lock_results()[0].is_err()); // Try locking a previously read-only account a 2nd time; should succeed @@ -7814,7 +7767,7 @@ pub(crate) mod tests { let tx = Transaction::new(&[&key2], message, genesis_config.hash()); let txs = vec![tx]; - let batch2 = bank.prepare_batch(&txs, None); + let batch2 = bank.prepare_batch(&txs); assert!(batch2.lock_results()[0].is_ok()); } @@ -9650,15 +9603,14 @@ pub(crate) mod tests { instructions, ); let txs = vec![tx0, tx1]; - let iteration_order: Vec = vec![0, 1]; - let batch = bank0.prepare_batch(&txs, Some(iteration_order)); + let batch = bank0.prepare_batch(&txs); let balances = bank0.collect_balances(&batch); assert_eq!(balances.len(), 2); assert_eq!(balances[0], vec![8, 11, 1]); assert_eq!(balances[1], vec![8, 0, 1]); - let iteration_order: Vec = vec![1, 0]; - let batch = bank0.prepare_batch(&txs, Some(iteration_order)); + let txs: Vec<_> = txs.iter().rev().cloned().collect(); + let batch = bank0.prepare_batch(&txs); let balances = bank0.collect_balances(&batch); assert_eq!(balances.len(), 2); assert_eq!(balances[0], vec![8, 0, 1]); @@ -9692,7 +9644,7 @@ pub(crate) mod tests { let tx2 = system_transaction::transfer(&keypair1, &pubkey2, 12, blockhash); let txs = vec![tx0, tx1, tx2]; - let lock_result = bank0.prepare_batch(&txs, None); + let lock_result = bank0.prepare_batch(&txs); let (transaction_results, transaction_balances_set, inner_instructions, transaction_logs) = bank0.load_execute_and_commit_transactions( &lock_result, @@ -12126,10 +12078,8 @@ pub(crate) mod tests { let success_sig = tx0.signatures[0]; let tx1 = system_transaction::transfer(&sender1, &recipient1, 110, blockhash); // Should produce insufficient funds log let failure_sig = tx1.signatures[0]; - let txs = vec![tx0, tx1]; - - // Use non-sequential batch ordering - let batch = bank.prepare_batch(&txs, Some(vec![1, 0])); + let txs = vec![tx1, tx0]; + let batch = bank.prepare_batch(&txs); let log_results = bank .load_execute_and_commit_transactions( diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a8fa95918..9d8af25d7 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -34,7 +34,6 @@ pub mod stakes; pub mod status_cache; mod system_instruction_processor; pub mod transaction_batch; -pub mod transaction_utils; pub mod vote_account; pub mod vote_sender_types; diff --git a/runtime/src/transaction_batch.rs b/runtime/src/transaction_batch.rs index 5002927dd..cc33ccd62 100644 --- a/runtime/src/transaction_batch.rs +++ b/runtime/src/transaction_batch.rs @@ -6,7 +6,6 @@ pub struct TransactionBatch<'a, 'b> { lock_results: Vec>, bank: &'a Bank, transactions: &'b [Transaction], - iteration_order: Option>, pub(crate) needs_unlock: bool, } @@ -15,17 +14,12 @@ impl<'a, 'b> TransactionBatch<'a, 'b> { lock_results: Vec>, bank: &'a Bank, transactions: &'b [Transaction], - iteration_order: Option>, ) -> Self { assert_eq!(lock_results.len(), transactions.len()); - if let Some(iteration_order) = &iteration_order { - assert_eq!(transactions.len(), iteration_order.len()); - } Self { lock_results, bank, transactions, - iteration_order, needs_unlock: true, } } @@ -38,14 +32,6 @@ impl<'a, 'b> TransactionBatch<'a, 'b> { self.transactions } - pub fn iteration_order(&self) -> Option<&[usize]> { - self.iteration_order.as_deref() - } - - pub fn iteration_order_vec(&self) -> Option> { - self.iteration_order.clone() - } - pub fn bank(&self) -> &Bank { self.bank } @@ -69,20 +55,20 @@ mod tests { let (bank, txs) = setup(); // Test getting locked accounts - let batch = bank.prepare_batch(&txs, None); + let batch = bank.prepare_batch(&txs); // Grab locks assert!(batch.lock_results().iter().all(|x| x.is_ok())); // Trying to grab locks again should fail - let batch2 = bank.prepare_batch(&txs, None); + let batch2 = bank.prepare_batch(&txs); assert!(batch2.lock_results().iter().all(|x| x.is_err())); // Drop the first set of locks drop(batch); // Now grabbing locks should work again - let batch2 = bank.prepare_batch(&txs, None); + let batch2 = bank.prepare_batch(&txs); assert!(batch2.lock_results().iter().all(|x| x.is_ok())); } @@ -95,7 +81,7 @@ mod tests { assert!(batch.lock_results().iter().all(|x| x.is_ok())); // Grab locks - let batch2 = bank.prepare_batch(&txs, None); + let batch2 = bank.prepare_batch(&txs); assert!(batch2.lock_results().iter().all(|x| x.is_ok())); // Prepare another batch without locks diff --git a/runtime/src/transaction_utils.rs b/runtime/src/transaction_utils.rs deleted file mode 100644 index 994163972..000000000 --- a/runtime/src/transaction_utils.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::ops::Index; - -/// OrderedIterator allows iterating with specific order specified -pub struct OrderedIterator<'a, T: 'a> { - element_order: Option<&'a [usize]>, - current: usize, - vec: &'a [T], -} - -impl<'a, T> OrderedIterator<'a, T> { - pub fn new(vec: &'a [T], element_order: Option<&'a [usize]>) -> OrderedIterator<'a, T> { - if let Some(custom_order) = element_order { - assert!(custom_order.len() == vec.len()); - } - OrderedIterator { - element_order, - current: 0, - vec, - } - } -} - -impl<'a, T> Iterator for OrderedIterator<'a, T> { - type Item = (usize, &'a T); - fn next(&mut self) -> Option { - if self.current >= self.vec.len() { - None - } else { - let index: usize; - if let Some(custom_order) = self.element_order { - index = custom_order[self.current]; - } else { - index = self.current; - } - self.current += 1; - Some((index, self.vec.index(index))) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - type IteratorResponse<'a> = Vec<(((usize, &'a usize), &'a usize), usize)>; - - #[test] - fn test_ordered_iterator_custom_order() { - let vec: Vec = vec![1, 2, 3, 4]; - let custom_order: Vec = vec![3, 1, 0, 2]; - let custom_order_ = custom_order.clone(); - let ordered_iterator = OrderedIterator::new(&vec, Some(&custom_order)); - let expected_response: Vec = vec![4, 2, 1, 3]; - - let resp: IteratorResponse = ordered_iterator - .zip(expected_response.iter()) - .zip(custom_order_) - .filter(|(((index, actual_elem), expected_elem), expected_index)| { - *actual_elem == *expected_elem && index == expected_index - }) - .collect(); - - assert_eq!(resp.len(), custom_order.len()); - } - - #[test] - fn test_ordered_iterator_original_order() { - let vec: Vec = vec![1, 2, 3, 4]; - let ordered_iterator = OrderedIterator::new(&vec, None); - - let resp: IteratorResponse = ordered_iterator - .zip(vec.iter()) - .zip(0..=4) - .filter(|(((index, actual_elem), expected_elem), expected_index)| { - *actual_elem == *expected_elem && index == expected_index - }) - .collect(); - - assert_eq!(resp.len(), vec.len()); - } -} diff --git a/transaction-status/src/token_balances.rs b/transaction-status/src/token_balances.rs index 8368bdc92..087ae99ff 100644 --- a/transaction-status/src/token_balances.rs +++ b/transaction-status/src/token_balances.rs @@ -2,9 +2,7 @@ use crate::TransactionTokenBalance; use solana_account_decoder::parse_token::{ spl_token_id_v2_0, spl_token_v2_0_native_mint, token_amount_to_ui_amount, UiTokenAmount, }; -use solana_runtime::{ - bank::Bank, transaction_batch::TransactionBatch, transaction_utils::OrderedIterator, -}; +use solana_runtime::{bank::Bank, transaction_batch::TransactionBatch}; use solana_sdk::{account::ReadableAccount, pubkey::Pubkey}; use spl_token_v2_0::{ solana_program::program_pack::Pack, @@ -57,7 +55,7 @@ pub fn collect_token_balances( ) -> TransactionTokenBalances { let mut balances: TransactionTokenBalances = vec![]; - for (_, transaction) in OrderedIterator::new(batch.transactions(), batch.iteration_order()) { + for transaction in batch.transactions() { let account_keys = &transaction.message.account_keys; let has_token_program = account_keys.iter().any(|p| is_token_program(p));