removes OrderedIterator and transaction batch iteration order (#16153)

In TransactionBatch,
https://github.com/solana-labs/solana/blob/e50f59844/runtime/src/transaction_batch.rs#L4-L11
lock_results[i] is aligned with transactions[iteration_order[i]]:
https://github.com/solana-labs/solana/blob/e50f59844/runtime/src/bank.rs#L2414-L2424
https://github.com/solana-labs/solana/blob/e50f59844/runtime/src/accounts.rs#L788-L817

However load_and_execute_transactions is iterating over
  lock_results[iteration_order[i]]
https://github.com/solana-labs/solana/blob/e50f59844/runtime/src/bank.rs#L2878-L2889
and then returning i as for the index of the retryable transaction.

If iteratorion_order is [1, 2, 0], and i is 0, then:
  lock_results[iteration_order[i]] = lock_results[1]
which corresponds to
  transactions[iteration_order[1]] = transactions[2]
so neither i = 0, nor iteration_order[i] = 1 gives the correct index for the
corresponding transaction (which is 2).

This commit removes OrderedIterator and transaction batch iteration order
entirely. There is only one place in blockstore processor which the
iteration order is not ordinal:
https://github.com/solana-labs/solana/blob/e50f59844/ledger/src/blockstore_processor.rs#L269-L271
It seems like, instead of using an iteration order, that can shuffle entry
transactions in-place.
This commit is contained in:
behzad nouri 2021-03-31 23:59:19 +00:00 committed by GitHub
parent ad7f8e7f23
commit 3f63ed9a72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 161 additions and 352 deletions

View File

@ -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)]

View File

@ -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)

View File

@ -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,

View File

@ -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,

View File

@ -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<Result<()>>,
) -> 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<Bank>,
entries: &[Entry],
entries: &mut [Entry],
randomize: bool,
transaction_status_sender: Option<TransactionStatusSender>,
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<Bank>,
entries: &[Entry],
entries: &mut [Entry],
randomize: bool,
entry_callback: Option<&ProcessCallback>,
transaction_status_sender: Option<TransactionStatusSender>,
@ -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<usize> = (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<Bank>,
pub transactions: Vec<Transaction>,
pub iteration_order: Option<Vec<usize>>,
pub statuses: Vec<TransactionExecutionResult>,
pub balances: TransactionBalancesSet,
pub token_balances: TransactionTokenBalancesSet,
@ -1132,7 +1125,6 @@ impl TransactionStatusSender {
&self,
bank: Arc<Bank>,
transactions: &[Transaction],
iteration_order: Option<Vec<usize>>,
statuses: Vec<TransactionExecutionResult>,
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<Arc<Bank>> = 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::<Vec<_>>(),
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<usize> = 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<Pubkey> = replay_vote_receiver
.try_iter()
.map(|(vote_pubkey, _, _)| vote_pubkey)

View File

@ -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);

View File

@ -269,7 +269,7 @@ fn process_transaction_and_record_inner(
) -> (Result<(), TransactionError>, Vec<Vec<CompiledInstruction>>) {
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<ConfirmedTransaction> {
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);

View File

@ -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<usize> = (0..100_usize).collect();
bencher.iter(|| {
let mut order: Vec<usize> = (0..100_usize).collect();
order.shuffle(&mut thread_rng());
let _ordered_iterator_resp: Vec<(usize, &usize)> =
OrderedIterator::new(&vec, Some(&order)).collect();
});
}

View File

@ -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<TransactionCheckResult>,
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<Result<()>> {
use solana_sdk::sanitize::Sanitize;
let keys: Vec<Result<_>> = OrderedIterator::new(txs, txs_iteration_order)
.map(|(_, tx)| {
let keys: Vec<Result<_>> = 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,

View File

@ -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<Vec<usize>>,
) -> 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<Result<()>>,
max_age: usize,
error_counters: &mut ErrorCounters,
) -> Vec<TransactionCheckResult> {
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<TransactionCheckResult>,
error_counters: &mut ErrorCounters,
) -> Vec<TransactionCheckResult> {
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<TransactionCheckResult>,
error_counters: &mut ErrorCounters,
) -> Vec<TransactionCheckResult> {
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<TransactionCheckResult> {
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<u64> = 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<TransactionExecutionResult> = 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<Result<()>> {
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<Result<()>> {
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<OverwrittenVoteAccount> {
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<usize> = 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<usize> = 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(

View File

@ -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;

View File

@ -6,7 +6,6 @@ pub struct TransactionBatch<'a, 'b> {
lock_results: Vec<Result<()>>,
bank: &'a Bank,
transactions: &'b [Transaction],
iteration_order: Option<Vec<usize>>,
pub(crate) needs_unlock: bool,
}
@ -15,17 +14,12 @@ impl<'a, 'b> TransactionBatch<'a, 'b> {
lock_results: Vec<Result<()>>,
bank: &'a Bank,
transactions: &'b [Transaction],
iteration_order: Option<Vec<usize>>,
) -> 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<Vec<usize>> {
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

View File

@ -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<Self::Item> {
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<usize> = vec![1, 2, 3, 4];
let custom_order: Vec<usize> = vec![3, 1, 0, 2];
let custom_order_ = custom_order.clone();
let ordered_iterator = OrderedIterator::new(&vec, Some(&custom_order));
let expected_response: Vec<usize> = 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<usize> = 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());
}
}

View File

@ -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));