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), hash: next_hash(&bank.last_blockhash(), 1, &tx_vector),
transactions: 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)] #[allow(clippy::same_item_push)]

View File

@ -754,7 +754,6 @@ impl BankingStage {
if num_to_commit != 0 { if num_to_commit != 0 {
let tx_results = bank.commit_transactions( let tx_results = bank.commit_transactions(
txs, txs,
None,
&mut loaded_accounts, &mut loaded_accounts,
&results, &results,
tx_count, tx_count,
@ -769,7 +768,6 @@ impl BankingStage {
transaction_status_sender.send_transaction_status_batch( transaction_status_sender.send_transaction_status_batch(
bank.clone(), bank.clone(),
batch.transactions(), batch.transactions(),
batch.iteration_order_vec(),
tx_results.execution_results, tx_results.execution_results,
TransactionBalancesSet::new(pre_balances, post_balances), TransactionBalancesSet::new(pre_balances, post_balances),
TransactionTokenBalancesSet::new(pre_token_balances, post_token_balances), TransactionTokenBalancesSet::new(pre_token_balances, post_token_balances),
@ -810,7 +808,7 @@ impl BankingStage {
let mut lock_time = Measure::start("lock_time"); let mut lock_time = Measure::start("lock_time");
// Once accounts are locked, other threads cannot encode transactions that will modify the // Once accounts are locked, other threads cannot encode transactions that will modify the
// same account state // same account state
let batch = bank.prepare_batch(txs, None); let batch = bank.prepare_batch(txs);
lock_time.stop(); lock_time.stop();
let (result, mut retryable_txs) = Self::process_and_record_transactions_locked( let (result, mut retryable_txs) = Self::process_and_record_transactions_locked(
@ -1003,7 +1001,6 @@ impl BankingStage {
}; };
let result = bank.check_transactions( let result = bank.check_transactions(
transactions, transactions,
None,
&filter, &filter,
(MAX_PROCESSING_AGE) (MAX_PROCESSING_AGE)
.saturating_sub(max_tx_fwd_delay) .saturating_sub(max_tx_fwd_delay)

View File

@ -3103,7 +3103,7 @@ pub(crate) mod tests {
let fail_tx = let fail_tx =
system_transaction::transfer(&mint_keypair, &keypair2.pubkey(), 2, Hash::default()); system_transaction::transfer(&mint_keypair, &keypair2.pubkey(), 2, Hash::default());
let entry_3 = next_entry(&entry_2.hash, 1, vec![fail_tx]); 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); let shreds = entries_to_test_shreds(entries.clone(), slot, previous_slot, true, 0);
blockstore.insert_shreds(shreds, None, false).unwrap(); blockstore.insert_shreds(shreds, None, false).unwrap();
@ -3122,7 +3122,7 @@ pub(crate) mod tests {
// that they are matched properly by get_rooted_block // that they are matched properly by get_rooted_block
let _result = blockstore_processor::process_entries( let _result = blockstore_processor::process_entries(
&bank, &bank,
&entries, &mut entries,
true, true,
Some(TransactionStatusSender { Some(TransactionStatusSender {
sender: transaction_status_sender, sender: transaction_status_sender,

View File

@ -4,9 +4,8 @@ use solana_ledger::{
blockstore::Blockstore, blockstore::Blockstore,
blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage}, blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage},
}; };
use solana_runtime::{ use solana_runtime::bank::{
bank::{Bank, InnerInstructionsList, NonceRollbackInfo, TransactionLogMessages}, Bank, InnerInstructionsList, NonceRollbackInfo, TransactionLogMessages,
transaction_utils::OrderedIterator,
}; };
use solana_transaction_status::{InnerInstructions, TransactionStatusMeta}; use solana_transaction_status::{InnerInstructions, TransactionStatusMeta};
use std::{ use std::{
@ -58,7 +57,6 @@ impl TransactionStatusService {
TransactionStatusMessage::Batch(TransactionStatusBatch { TransactionStatusMessage::Batch(TransactionStatusBatch {
bank, bank,
transactions, transactions,
iteration_order,
statuses, statuses,
balances, balances,
token_balances, token_balances,
@ -80,7 +78,7 @@ impl TransactionStatusService {
Box::new(std::iter::repeat_with(Vec::new)) Box::new(std::iter::repeat_with(Vec::new))
}; };
for ( for (
(_, transaction), transaction,
(status, nonce_rollback), (status, nonce_rollback),
pre_balances, pre_balances,
post_balances, post_balances,
@ -89,7 +87,7 @@ impl TransactionStatusService {
inner_instructions, inner_instructions,
log_messages, log_messages,
) in izip!( ) in izip!(
OrderedIterator::new(&transactions, iteration_order.as_deref()), &transactions,
statuses, statuses,
balances.pre_balances, balances.pre_balances,
balances.post_balances, balances.post_balances,

View File

@ -25,7 +25,6 @@ use solana_runtime::{
bank_utils, bank_utils,
commitment::VOTE_THRESHOLD_SIZE, commitment::VOTE_THRESHOLD_SIZE,
transaction_batch::TransactionBatch, transaction_batch::TransactionBatch,
transaction_utils::OrderedIterator,
vote_account::ArcVoteAccount, vote_account::ArcVoteAccount,
vote_sender_types::ReplayVoteSender, vote_sender_types::ReplayVoteSender,
}; };
@ -76,10 +75,7 @@ fn get_first_error(
fee_collection_results: Vec<Result<()>>, fee_collection_results: Vec<Result<()>>,
) -> Option<(Result<()>, Signature)> { ) -> Option<(Result<()>, Signature)> {
let mut first_err = None; let mut first_err = None;
for (result, (_, transaction)) in fee_collection_results.iter().zip(OrderedIterator::new( for (result, transaction) in fee_collection_results.iter().zip(batch.transactions()) {
batch.transactions(),
batch.iteration_order(),
)) {
if let Err(ref err) = result { if let Err(ref err) = result {
if first_err.is_none() { if first_err.is_none() {
first_err = Some((result.clone(), transaction.signatures[0])); first_err = Some((result.clone(), transaction.signatures[0]));
@ -149,7 +145,6 @@ fn execute_batch(
transaction_status_sender.send_transaction_status_batch( transaction_status_sender.send_transaction_status_batch(
bank.clone(), bank.clone(),
batch.transactions(), batch.transactions(),
batch.iteration_order_vec(),
execution_results, execution_results,
balances, balances,
token_balances, token_balances,
@ -208,7 +203,7 @@ fn execute_batches(
/// 4. Update the leader scheduler, goto 1 /// 4. Update the leader scheduler, goto 1
pub fn process_entries( pub fn process_entries(
bank: &Arc<Bank>, bank: &Arc<Bank>,
entries: &[Entry], entries: &mut [Entry],
randomize: bool, randomize: bool,
transaction_status_sender: Option<TransactionStatusSender>, transaction_status_sender: Option<TransactionStatusSender>,
replay_vote_sender: Option<&ReplayVoteSender>, replay_vote_sender: Option<&ReplayVoteSender>,
@ -228,9 +223,10 @@ pub fn process_entries(
result result
} }
// Note: If randomize is true this will shuffle entries' transactions in-place.
fn process_entries_with_callback( fn process_entries_with_callback(
bank: &Arc<Bank>, bank: &Arc<Bank>,
entries: &[Entry], entries: &mut [Entry],
randomize: bool, randomize: bool,
entry_callback: Option<&ProcessCallback>, entry_callback: Option<&ProcessCallback>,
transaction_status_sender: Option<TransactionStatusSender>, transaction_status_sender: Option<TransactionStatusSender>,
@ -240,6 +236,12 @@ fn process_entries_with_callback(
// accumulator for entries that can be processed in parallel // accumulator for entries that can be processed in parallel
let mut batches = vec![]; let mut batches = vec![];
let mut tick_hashes = 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 { for entry in entries {
if entry.is_tick() { if entry.is_tick() {
// If it's a tick, save it for later // If it's a tick, save it for later
@ -265,17 +267,8 @@ fn process_entries_with_callback(
} }
// else loop on processing the entry // else loop on processing the entry
loop { 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 // 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()); let first_lock_err = first_err(batch.lock_results());
// if locking worked // if locking worked
@ -677,7 +670,7 @@ pub fn confirm_slot(
) -> result::Result<(), BlockstoreProcessorError> { ) -> result::Result<(), BlockstoreProcessorError> {
let slot = bank.slot(); 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 mut load_elapsed = Measure::start("load_elapsed");
let load_result = blockstore let load_result = blockstore
.get_slot_entries_with_shred_info(slot, progress.num_shreds, allow_dead_slots) .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 replay_elapsed = Measure::start("replay_elapsed");
let mut execute_timings = ExecuteTimings::default(); let mut execute_timings = ExecuteTimings::default();
// Note: This will shuffle entries' transactions in-place.
let process_result = process_entries_with_callback( let process_result = process_entries_with_callback(
bank, bank,
&entries, &mut entries,
true, true, // shuffle transactions.
entry_callback, entry_callback,
transaction_status_sender, transaction_status_sender,
replay_vote_sender, replay_vote_sender,
@ -1113,7 +1107,6 @@ pub enum TransactionStatusMessage {
pub struct TransactionStatusBatch { pub struct TransactionStatusBatch {
pub bank: Arc<Bank>, pub bank: Arc<Bank>,
pub transactions: Vec<Transaction>, pub transactions: Vec<Transaction>,
pub iteration_order: Option<Vec<usize>>,
pub statuses: Vec<TransactionExecutionResult>, pub statuses: Vec<TransactionExecutionResult>,
pub balances: TransactionBalancesSet, pub balances: TransactionBalancesSet,
pub token_balances: TransactionTokenBalancesSet, pub token_balances: TransactionTokenBalancesSet,
@ -1132,7 +1125,6 @@ impl TransactionStatusSender {
&self, &self,
bank: Arc<Bank>, bank: Arc<Bank>,
transactions: &[Transaction], transactions: &[Transaction],
iteration_order: Option<Vec<usize>>,
statuses: Vec<TransactionExecutionResult>, statuses: Vec<TransactionExecutionResult>,
balances: TransactionBalancesSet, balances: TransactionBalancesSet,
token_balances: TransactionTokenBalancesSet, token_balances: TransactionTokenBalancesSet,
@ -1150,7 +1142,6 @@ impl TransactionStatusSender {
.send(TransactionStatusMessage::Batch(TransactionStatusBatch { .send(TransactionStatusMessage::Batch(TransactionStatusBatch {
bank, bank,
transactions: transactions.to_vec(), transactions: transactions.to_vec(),
iteration_order,
statuses, statuses,
balances, balances,
token_balances, token_balances,
@ -1897,7 +1888,8 @@ pub mod tests {
} = create_genesis_config(2); } = create_genesis_config(2);
let bank = Arc::new(Bank::new(&genesis_config)); let bank = Arc::new(Bank::new(&genesis_config));
let keypair = Keypair::new(); 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( let tx = system_transaction::transfer(
&mint_keypair, &mint_keypair,
&keypair.pubkey(), &keypair.pubkey(),
@ -1912,7 +1904,7 @@ pub mod tests {
); );
// Now ensure the TX is accepted despite pointing to the ID of an empty entry. // 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(())); assert_eq!(bank.process_transaction(&tx), Ok(()));
} }
@ -2117,7 +2109,10 @@ pub mod tests {
// ensure bank can process a tick // ensure bank can process a tick
assert_eq!(bank.tick_height(), 0); assert_eq!(bank.tick_height(), 0);
let tick = next_entry(&genesis_config.hash(), 1, vec![]); 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); assert_eq!(bank.tick_height(), 1);
} }
@ -2150,7 +2145,7 @@ pub mod tests {
); );
let entry_2 = next_entry(&entry_1.hash, 1, vec![tx]); let entry_2 = next_entry(&entry_1.hash, 1, vec![tx]);
assert_eq!( assert_eq!(
process_entries(&bank, &[entry_1, entry_2], true, None, None), process_entries(&bank, &mut [entry_1, entry_2], true, None, None),
Ok(()) Ok(())
); );
assert_eq!(bank.get_balance(&keypair1.pubkey()), 2); assert_eq!(bank.get_balance(&keypair1.pubkey()), 2);
@ -2208,7 +2203,7 @@ pub mod tests {
assert_eq!( assert_eq!(
process_entries( process_entries(
&bank, &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, false,
None, None,
None, None,
@ -2280,7 +2275,7 @@ pub mod tests {
assert!(process_entries( assert!(process_entries(
&bank, &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, false,
None, None,
None, None,
@ -2294,13 +2289,13 @@ pub mod tests {
// Check all accounts are unlocked // Check all accounts are unlocked
let txs1 = &entry_1_to_mint.transactions[..]; let txs1 = &entry_1_to_mint.transactions[..];
let txs2 = &entry_2_to_3_mint_to_1.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() { for result in batch1.lock_results() {
assert!(result.is_ok()); assert!(result.is_ok());
} }
// txs1 and txs2 have accounts that conflict, so we must drop txs1 first // txs1 and txs2 have accounts that conflict, so we must drop txs1 first
drop(batch1); drop(batch1);
let batch2 = bank.prepare_batch(txs2, None); let batch2 = bank.prepare_batch(txs2);
for result in batch2.lock_results() { for result in batch2.lock_results() {
assert!(result.is_ok()); assert!(result.is_ok());
} }
@ -2388,7 +2383,7 @@ pub mod tests {
assert!(process_entries( assert!(process_entries(
&bank, &bank,
&[ &mut [
entry_1_to_mint, entry_1_to_mint,
entry_2_to_3_and_1_to_mint, entry_2_to_3_and_1_to_mint,
entry_conflict_itself, entry_conflict_itself,
@ -2443,7 +2438,7 @@ pub mod tests {
system_transaction::transfer(&keypair2, &keypair4.pubkey(), 1, bank.last_blockhash()); system_transaction::transfer(&keypair2, &keypair4.pubkey(), 1, bank.last_blockhash());
let entry_2 = next_entry(&entry_1.hash, 1, vec![tx]); let entry_2 = next_entry(&entry_1.hash, 1, vec![tx]);
assert_eq!( assert_eq!(
process_entries(&bank, &[entry_1, entry_2], true, None, None), process_entries(&bank, &mut [entry_1, entry_2], true, None, None),
Ok(()) Ok(())
); );
assert_eq!(bank.get_balance(&keypair3.pubkey()), 1); assert_eq!(bank.get_balance(&keypair3.pubkey()), 1);
@ -2477,7 +2472,7 @@ pub mod tests {
let present_account = AccountSharedData::new(1, 10, &Pubkey::default()); let present_account = AccountSharedData::new(1, 10, &Pubkey::default());
bank.store_account(&present_account_key.pubkey(), &present_account); 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) .step_by(NUM_TRANSFERS_PER_ENTRY)
.map(|i| { .map(|i| {
let mut transactions = (0..NUM_TRANSFERS_PER_ENTRY) let mut transactions = (0..NUM_TRANSFERS_PER_ENTRY)
@ -2503,7 +2498,10 @@ pub mod tests {
next_entry_mut(&mut hash, 0, transactions) next_entry_mut(&mut hash, 0, transactions)
}) })
.collect(); .collect();
assert_eq!(process_entries(&bank, &entries, true, None, None), Ok(())); assert_eq!(
process_entries(&bank, &mut entries, true, None, None),
Ok(())
);
} }
#[test] #[test]
@ -2563,7 +2561,10 @@ pub mod tests {
// Transfer lamports to each other // Transfer lamports to each other
let entry = next_entry(&bank.last_blockhash(), 1, tx_vector); 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(); bank.squash();
// Even number keypair should have balance of 2 * initial_lamports and // 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()); system_transaction::transfer(&keypair1, &keypair4.pubkey(), 1, bank.last_blockhash());
let entry_2 = next_entry(&tick.hash, 1, vec![tx]); let entry_2 = next_entry(&tick.hash, 1, vec![tx]);
assert_eq!( 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(()) Ok(())
); );
assert_eq!(bank.get_balance(&keypair3.pubkey()), 1); 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()); system_transaction::transfer(&keypair2, &keypair3.pubkey(), 1, bank.last_blockhash());
let entry_3 = next_entry(&entry_2.hash, 1, vec![tx]); let entry_3 = next_entry(&entry_2.hash, 1, vec![tx]);
assert_eq!( assert_eq!(
process_entries(&bank, &[entry_3], true, None, None), process_entries(&bank, &mut [entry_3], true, None, None),
Err(TransactionError::AccountNotFound) Err(TransactionError::AccountNotFound)
); );
} }
@ -2712,7 +2719,7 @@ pub mod tests {
); );
assert_eq!( 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) Err(TransactionError::AccountInUse)
); );
@ -2863,7 +2870,7 @@ pub mod tests {
let mut hash = bank.last_blockhash(); let mut hash = bank.last_blockhash();
let mut root: Option<Arc<Bank>> = None; let mut root: Option<Arc<Bank>> = None;
loop { loop {
let entries: Vec<_> = (0..NUM_TRANSFERS) let mut entries: Vec<_> = (0..NUM_TRANSFERS)
.step_by(NUM_TRANSFERS_PER_ENTRY) .step_by(NUM_TRANSFERS_PER_ENTRY)
.map(|i| { .map(|i| {
next_entry_mut(&mut hash, 0, { next_entry_mut(&mut hash, 0, {
@ -2891,9 +2898,9 @@ pub mod tests {
}) })
.collect(); .collect();
info!("paying iteration {}", i); 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) .step_by(NUM_TRANSFERS_PER_ENTRY)
.map(|i| { .map(|i| {
next_entry_mut( next_entry_mut(
@ -2914,12 +2921,12 @@ pub mod tests {
.collect(); .collect();
info!("refunding iteration {}", i); 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 // advance to next block
process_entries( process_entries(
&bank, &bank,
&(0..bank.ticks_per_slot()) &mut (0..bank.ticks_per_slot())
.map(|_| next_entry_mut(&mut hash, 1, vec![])) .map(|_| next_entry_mut(&mut hash, 1, vec![]))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
true, true,
@ -2969,7 +2976,7 @@ pub mod tests {
process_entries_with_callback( process_entries_with_callback(
&bank0, &bank0,
&entries, &mut entries,
true, true,
None, None,
None, None,
@ -3049,12 +3056,8 @@ pub mod tests {
bank.last_blockhash(), bank.last_blockhash(),
); );
account_loaded_twice.message.account_keys[1] = mint_keypair.pubkey(); account_loaded_twice.message.account_keys[1] = mint_keypair.pubkey();
let transactions = [account_loaded_twice, account_not_found_tx]; let transactions = [account_not_found_tx, account_loaded_twice];
let batch = bank.prepare_batch(&transactions);
// Use inverted iteration_order
let iteration_order: Vec<usize> = vec![1, 0];
let batch = bank.prepare_batch(&transactions, Some(iteration_order));
let ( let (
TransactionResults { TransactionResults {
fee_collection_results, fee_collection_results,
@ -3150,7 +3153,7 @@ pub mod tests {
.collect(); .collect();
let entry = next_entry(&bank_1_blockhash, 1, vote_txs); let entry = next_entry(&bank_1_blockhash, 1, vote_txs);
let (replay_vote_sender, replay_vote_receiver) = unbounded(); 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 let successes: BTreeSet<Pubkey> = replay_vote_receiver
.try_iter() .try_iter()
.map(|(vote_pubkey, _, _)| vote_pubkey) .map(|(vote_pubkey, _, _)| vote_pubkey)

View File

@ -375,12 +375,11 @@ fn setup_fee_calculator(bank: Bank) -> Bank {
// initialized with a non-zero fee. // initialized with a non-zero fee.
assert_eq!(bank.signature_count(), 0); assert_eq!(bank.signature_count(), 0);
bank.commit_transactions( bank.commit_transactions(
&[], &[], // transactions
None, &mut [], // loaded accounts
&mut [], &[], // transaction execution results
&[], 0, // tx count
0, 1, // signature count
1,
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
); );
assert_eq!(bank.signature_count(), 1); assert_eq!(bank.signature_count(), 1);

View File

@ -269,7 +269,7 @@ fn process_transaction_and_record_inner(
) -> (Result<(), TransactionError>, Vec<Vec<CompiledInstruction>>) { ) -> (Result<(), TransactionError>, Vec<Vec<CompiledInstruction>>) {
let signature = tx.signatures.get(0).unwrap().clone(); let signature = tx.signatures.get(0).unwrap().clone();
let txs = vec![tx]; 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( let (mut results, _, mut inner, _transaction_logs) = bank.load_execute_and_commit_transactions(
&tx_batch, &tx_batch,
MAX_PROCESSING_AGE, MAX_PROCESSING_AGE,
@ -294,7 +294,7 @@ fn process_transaction_and_record_inner(
} }
fn execute_transactions(bank: &Bank, txs: &[Transaction]) -> Vec<ConfirmedTransaction> { 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 timings = ExecuteTimings::default();
let mut mint_decimals = HashMap::new(); let mut mint_decimals = HashMap::new();
let tx_pre_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals); 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, blockhash_queue::BlockhashQueue,
rent_collector::RentCollector, rent_collector::RentCollector,
system_instruction_processor::{get_system_account_kind, SystemAccountKind}, system_instruction_processor::{get_system_account_kind, SystemAccountKind},
transaction_utils::OrderedIterator,
}; };
use dashmap::{ use dashmap::{
mapref::entry::Entry::{Occupied, Vacant}, mapref::entry::Entry::{Occupied, Vacant},
@ -391,7 +390,6 @@ impl Accounts {
&self, &self,
ancestors: &Ancestors, ancestors: &Ancestors,
txs: &[Transaction], txs: &[Transaction],
txs_iteration_order: Option<&[usize]>,
lock_results: Vec<TransactionCheckResult>, lock_results: Vec<TransactionCheckResult>,
hash_queue: &BlockhashQueue, hash_queue: &BlockhashQueue,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
@ -402,10 +400,10 @@ impl Accounts {
secp256k1_program_enabled: feature_set secp256k1_program_enabled: feature_set
.is_active(&feature_set::secp256k1_program_enabled::id()), .is_active(&feature_set::secp256k1_program_enabled::id()),
}; };
OrderedIterator::new(txs, txs_iteration_order) txs.iter()
.zip(lock_results.into_iter()) .zip(lock_results)
.map(|etx| match etx { .map(|etx| match etx {
((_, tx), (Ok(()), nonce_rollback)) => { (tx, (Ok(()), nonce_rollback)) => {
let fee_calculator = nonce_rollback let fee_calculator = nonce_rollback
.as_ref() .as_ref()
.map(|nonce_rollback| nonce_rollback.fee_calculator()) .map(|nonce_rollback| nonce_rollback.fee_calculator())
@ -804,12 +802,12 @@ impl Accounts {
pub fn lock_accounts( pub fn lock_accounts(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
txs_iteration_order: Option<&[usize]>,
demote_sysvar_write_locks: bool, demote_sysvar_write_locks: bool,
) -> Vec<Result<()>> { ) -> Vec<Result<()>> {
use solana_sdk::sanitize::Sanitize; use solana_sdk::sanitize::Sanitize;
let keys: Vec<Result<_>> = OrderedIterator::new(txs, txs_iteration_order) let keys: Vec<Result<_>> = txs
.map(|(_, tx)| { .iter()
.map(|tx| {
tx.sanitize().map_err(TransactionError::from)?; tx.sanitize().map_err(TransactionError::from)?;
if Self::has_duplicates(&tx.message.account_keys) { if Self::has_duplicates(&tx.message.account_keys) {
@ -836,18 +834,19 @@ impl Accounts {
pub fn unlock_accounts( pub fn unlock_accounts(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
txs_iteration_order: Option<&[usize]>,
results: &[Result<()>], results: &[Result<()>],
demote_sysvar_write_locks: bool, demote_sysvar_write_locks: bool,
) { ) {
let mut account_locks = self.account_locks.lock().unwrap(); let mut account_locks = self.account_locks.lock().unwrap();
debug!("bank unlock accounts"); debug!("bank unlock accounts");
for (tx, lock_result) in txs.iter().zip(results) {
OrderedIterator::new(txs, txs_iteration_order) self.unlock_account(
.zip(results.iter()) tx,
.for_each(|((_, tx), result)| { lock_result,
self.unlock_account(tx, result, &mut account_locks, demote_sysvar_write_locks) &mut account_locks,
}); demote_sysvar_write_locks,
);
}
} }
/// Store the accounts into the DB /// Store the accounts into the DB
@ -857,7 +856,6 @@ impl Accounts {
&self, &self,
slot: Slot, slot: Slot,
txs: &[Transaction], txs: &[Transaction],
txs_iteration_order: Option<&[usize]>,
res: &[TransactionExecutionResult], res: &[TransactionExecutionResult],
loaded: &mut [TransactionLoadResult], loaded: &mut [TransactionLoadResult],
rent_collector: &RentCollector, rent_collector: &RentCollector,
@ -867,7 +865,6 @@ impl Accounts {
) { ) {
let accounts_to_store = self.collect_accounts_to_store( let accounts_to_store = self.collect_accounts_to_store(
txs, txs,
txs_iteration_order,
res, res,
loaded, loaded,
rent_collector, rent_collector,
@ -892,7 +889,6 @@ impl Accounts {
fn collect_accounts_to_store<'a>( fn collect_accounts_to_store<'a>(
&self, &self,
txs: &'a [Transaction], txs: &'a [Transaction],
txs_iteration_order: Option<&'a [usize]>,
res: &'a [TransactionExecutionResult], res: &'a [TransactionExecutionResult],
loaded: &'a mut [TransactionLoadResult], loaded: &'a mut [TransactionLoadResult],
rent_collector: &RentCollector, rent_collector: &RentCollector,
@ -901,11 +897,7 @@ impl Accounts {
demote_sysvar_write_locks: bool, demote_sysvar_write_locks: bool,
) -> Vec<(&'a Pubkey, &'a AccountSharedData)> { ) -> Vec<(&'a Pubkey, &'a AccountSharedData)> {
let mut accounts = Vec::with_capacity(loaded.len()); let mut accounts = Vec::with_capacity(loaded.len());
for (i, ((raccs, _nonce_rollback), (_, tx))) in loaded for (i, ((raccs, _nonce_rollback), tx)) in loaded.iter_mut().zip(txs).enumerate() {
.iter_mut()
.zip(OrderedIterator::new(txs, txs_iteration_order))
.enumerate()
{
if raccs.is_err() { if raccs.is_err() {
continue; continue;
} }
@ -1086,7 +1078,6 @@ mod tests {
accounts.load_accounts( accounts.load_accounts(
&ancestors, &ancestors,
&[tx], &[tx],
None,
vec![(Ok(()), None)], vec![(Ok(()), None)],
&hash_queue, &hash_queue,
error_counters, error_counters,
@ -1689,7 +1680,6 @@ mod tests {
let tx = Transaction::new(&[&keypair0], message, Hash::default()); let tx = Transaction::new(&[&keypair0], message, Hash::default());
let results0 = accounts.lock_accounts( let results0 = accounts.lock_accounts(
&[tx.clone()], &[tx.clone()],
None, // txs_iteration_order
true, // demote_sysvar_write_locks true, // demote_sysvar_write_locks
); );
@ -1727,8 +1717,7 @@ mod tests {
let tx1 = Transaction::new(&[&keypair1], message, Hash::default()); let tx1 = Transaction::new(&[&keypair1], message, Hash::default());
let txs = vec![tx0, tx1]; let txs = vec![tx0, tx1];
let results1 = accounts.lock_accounts( let results1 = accounts.lock_accounts(
&txs, None, // txs_iteration_order &txs, true, // demote_sysvar_write_locks
true, // demote_sysvar_write_locks
); );
assert!(results1[0].is_ok()); // Read-only account (keypair1) can be referenced multiple times assert!(results1[0].is_ok()); // Read-only account (keypair1) can be referenced multiple times
@ -1746,13 +1735,11 @@ mod tests {
accounts.unlock_accounts( accounts.unlock_accounts(
&[tx], &[tx],
None, // txs_iteration_order
&results0, &results0,
true, // demote_sysvar_write_locks true, // demote_sysvar_write_locks
); );
accounts.unlock_accounts( accounts.unlock_accounts(
&txs, None, // txs_iteration_order &txs, &results1, true, // demote_sysvar_write_locks
&results1, true, // demote_sysvar_write_locks
); );
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])]; let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
let message = Message::new_with_compiled_instructions( let message = Message::new_with_compiled_instructions(
@ -1766,7 +1753,6 @@ mod tests {
let tx = Transaction::new(&[&keypair1], message, Hash::default()); let tx = Transaction::new(&[&keypair1], message, Hash::default());
let results2 = accounts.lock_accounts( let results2 = accounts.lock_accounts(
&[tx], &[tx],
None, // txs_iteration_order
true, // demote_sysvar_write_locks true, // demote_sysvar_write_locks
); );
assert!(results2[0].is_ok()); // Now keypair1 account can be locked as writable assert!(results2[0].is_ok()); // Now keypair1 account can be locked as writable
@ -1833,8 +1819,7 @@ mod tests {
loop { loop {
let txs = vec![writable_tx.clone()]; let txs = vec![writable_tx.clone()];
let results = accounts_clone.clone().lock_accounts( let results = accounts_clone.clone().lock_accounts(
&txs, None, // txs_iteration_order &txs, true, // demote_sysvar_write_locks
true, // demote_sysvar_write_locks
); );
for result in results.iter() { for result in results.iter() {
if result.is_ok() { if result.is_ok() {
@ -1842,8 +1827,7 @@ mod tests {
} }
} }
accounts_clone.unlock_accounts( accounts_clone.unlock_accounts(
&txs, None, // txs_iteration_order &txs, &results, true, // demote_sysvar_write_locks
&results, true, // demote_sysvar_write_locks
); );
if exit_clone.clone().load(Ordering::Relaxed) { if exit_clone.clone().load(Ordering::Relaxed) {
break; break;
@ -1854,8 +1838,7 @@ mod tests {
for _ in 0..5 { for _ in 0..5 {
let txs = vec![readonly_tx.clone()]; let txs = vec![readonly_tx.clone()];
let results = accounts_arc.clone().lock_accounts( let results = accounts_arc.clone().lock_accounts(
&txs, None, // txs_iteration_order &txs, true, // demote_sysvar_write_locks
true, // demote_sysvar_write_locks
); );
if results[0].is_ok() { if results[0].is_ok() {
let counter_value = counter_clone.clone().load(Ordering::SeqCst); 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)); assert_eq!(counter_value, counter_clone.clone().load(Ordering::SeqCst));
} }
accounts_arc.unlock_accounts( accounts_arc.unlock_accounts(
&txs, None, // txs_iteration_order &txs, &results, true, // demote_sysvar_write_locks
&results, true, // demote_sysvar_write_locks
); );
thread::sleep(time::Duration::from_millis(50)); thread::sleep(time::Duration::from_millis(50));
} }
@ -1947,7 +1929,6 @@ mod tests {
} }
let collected_accounts = accounts.collect_accounts_to_store( let collected_accounts = accounts.collect_accounts_to_store(
&txs, &txs,
None,
&loaders, &loaders,
loaded.as_mut_slice(), loaded.as_mut_slice(),
&rent_collector, &rent_collector,
@ -2017,7 +1998,6 @@ mod tests {
accounts.load_accounts( accounts.load_accounts(
&ancestors, &ancestors,
&[tx], &[tx],
None,
vec![(Ok(()), None)], vec![(Ok(()), None)],
&hash_queue, &hash_queue,
&mut error_counters, &mut error_counters,
@ -2313,7 +2293,6 @@ mod tests {
Accounts::new_with_config(Vec::new(), &ClusterType::Development, HashSet::new(), false); Accounts::new_with_config(Vec::new(), &ClusterType::Development, HashSet::new(), false);
let collected_accounts = accounts.collect_accounts_to_store( let collected_accounts = accounts.collect_accounts_to_store(
&txs, &txs,
None,
&loaders, &loaders,
loaded.as_mut_slice(), loaded.as_mut_slice(),
&rent_collector, &rent_collector,
@ -2425,7 +2404,6 @@ mod tests {
Accounts::new_with_config(Vec::new(), &ClusterType::Development, HashSet::new(), false); Accounts::new_with_config(Vec::new(), &ClusterType::Development, HashSet::new(), false);
let collected_accounts = accounts.collect_accounts_to_store( let collected_accounts = accounts.collect_accounts_to_store(
&txs, &txs,
None,
&loaders, &loaders,
loaded.as_mut_slice(), loaded.as_mut_slice(),
&rent_collector, &rent_collector,

View File

@ -21,7 +21,6 @@ use crate::{
status_cache::{SlotDelta, StatusCache}, status_cache::{SlotDelta, StatusCache},
system_instruction_processor::{get_system_account_kind, SystemAccountKind}, system_instruction_processor::{get_system_account_kind, SystemAccountKind},
transaction_batch::TransactionBatch, transaction_batch::TransactionBatch,
transaction_utils::OrderedIterator,
vote_account::ArcVoteAccount, vote_account::ArcVoteAccount,
}; };
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
@ -2347,15 +2346,10 @@ impl Bank {
} }
} }
fn update_transaction_statuses( fn update_transaction_statuses(&self, txs: &[Transaction], res: &[TransactionExecutionResult]) {
&self,
txs: &[Transaction],
iteration_order: Option<&[usize]>,
res: &[TransactionExecutionResult],
) {
let mut status_cache = self.src.status_cache.write().unwrap(); let mut status_cache = self.src.status_cache.write().unwrap();
for (i, (_, tx)) in OrderedIterator::new(txs, iteration_order).enumerate() { assert_eq!(txs.len(), res.len());
let (res, _nonce_rollback) = &res[i]; for (tx, (res, _nonce_rollback)) in txs.iter().zip(res) {
if Self::can_commit(res) && !tx.signatures.is_empty() { if Self::can_commit(res) && !tx.signatures.is_empty() {
status_cache.insert( status_cache.insert(
&tx.message().recent_blockhash, &tx.message().recent_blockhash,
@ -2416,17 +2410,12 @@ impl Bank {
.is_active(&feature_set::demote_sysvar_write_locks::id()) .is_active(&feature_set::demote_sysvar_write_locks::id())
} }
pub fn prepare_batch<'a, 'b>( pub fn prepare_batch<'a, 'b>(&'a self, txs: &'b [Transaction]) -> TransactionBatch<'a, 'b> {
&'a self, let lock_results = self
txs: &'b [Transaction], .rc
iteration_order: Option<Vec<usize>>, .accounts
) -> TransactionBatch<'a, 'b> { .lock_accounts(txs, self.demote_sysvar_write_locks());
let results = self.rc.accounts.lock_accounts( TransactionBatch::new(lock_results, &self, txs)
txs,
iteration_order.as_deref(),
self.demote_sysvar_write_locks(),
);
TransactionBatch::new(results, &self, txs, iteration_order)
} }
pub fn prepare_simulation_batch<'a, 'b>( pub fn prepare_simulation_batch<'a, 'b>(
@ -2437,7 +2426,7 @@ impl Bank {
.iter() .iter()
.map(|tx| tx.sanitize().map_err(|e| e.into())) .map(|tx| tx.sanitize().map_err(|e| e.into()))
.collect(); .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.needs_unlock = false;
batch batch
} }
@ -2488,7 +2477,6 @@ impl Bank {
batch.needs_unlock = false; batch.needs_unlock = false;
self.rc.accounts.unlock_accounts( self.rc.accounts.unlock_accounts(
batch.transactions(), batch.transactions(),
batch.iteration_order(),
batch.lock_results(), batch.lock_results(),
self.demote_sysvar_write_locks(), self.demote_sysvar_write_locks(),
) )
@ -2506,15 +2494,14 @@ impl Bank {
fn check_age( fn check_age(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
iteration_order: Option<&[usize]>,
lock_results: Vec<Result<()>>, lock_results: Vec<Result<()>>,
max_age: usize, max_age: usize,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Vec<TransactionCheckResult> { ) -> Vec<TransactionCheckResult> {
let hash_queue = self.blockhash_queue.read().unwrap(); let hash_queue = self.blockhash_queue.read().unwrap();
OrderedIterator::new(txs, iteration_order) txs.iter()
.zip(lock_results.into_iter()) .zip(lock_results)
.map(|((_, tx), lock_res)| match lock_res { .map(|(tx, lock_res)| match lock_res {
Ok(()) => { Ok(()) => {
let message = tx.message(); let message = tx.message();
let hash_age = hash_queue.check_hash_age(&message.recent_blockhash, max_age); let hash_age = hash_queue.check_hash_age(&message.recent_blockhash, max_age);
@ -2538,14 +2525,13 @@ impl Bank {
fn check_signatures( fn check_signatures(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
iteration_order: Option<&[usize]>,
lock_results: Vec<TransactionCheckResult>, lock_results: Vec<TransactionCheckResult>,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Vec<TransactionCheckResult> { ) -> Vec<TransactionCheckResult> {
let rcache = self.src.status_cache.read().unwrap(); let rcache = self.src.status_cache.read().unwrap();
OrderedIterator::new(txs, iteration_order) txs.iter()
.zip(lock_results.into_iter()) .zip(lock_results)
.map(|((_, tx), lock_res)| { .map(|(tx, lock_res)| {
if tx.signatures.is_empty() { if tx.signatures.is_empty() {
return lock_res; return lock_res;
} }
@ -2572,13 +2558,12 @@ impl Bank {
fn filter_by_vote_transactions( fn filter_by_vote_transactions(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
iteration_order: Option<&[usize]>,
lock_results: Vec<TransactionCheckResult>, lock_results: Vec<TransactionCheckResult>,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Vec<TransactionCheckResult> { ) -> Vec<TransactionCheckResult> {
OrderedIterator::new(txs, iteration_order) txs.iter()
.zip(lock_results.into_iter()) .zip(lock_results)
.map(|((_, tx), lock_res)| { .map(|(tx, lock_res)| {
if lock_res.0.is_ok() { if lock_res.0.is_ok() {
if is_simple_vote_transaction(tx) { if is_simple_vote_transaction(tx) {
return lock_res; return lock_res;
@ -2627,28 +2612,15 @@ impl Bank {
pub fn check_transactions( pub fn check_transactions(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
iteration_order: Option<&[usize]>,
lock_results: &[Result<()>], lock_results: &[Result<()>],
max_age: usize, max_age: usize,
mut error_counters: &mut ErrorCounters, mut error_counters: &mut ErrorCounters,
) -> Vec<TransactionCheckResult> { ) -> Vec<TransactionCheckResult> {
let age_results = self.check_age( let age_results = self.check_age(txs, lock_results.to_vec(), max_age, &mut error_counters);
txs, let sigcheck_results = self.check_signatures(txs, age_results, &mut error_counters);
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);
if self.upgrade_epoch() { if self.upgrade_epoch() {
// Reject all non-vote transactions // Reject all non-vote transactions
self.filter_by_vote_transactions( self.filter_by_vote_transactions(txs, sigcheck_results, &mut error_counters)
txs,
iteration_order,
sigcheck_results,
&mut error_counters,
)
} else { } else {
sigcheck_results sigcheck_results
} }
@ -2656,8 +2628,7 @@ impl Bank {
pub fn collect_balances(&self, batch: &TransactionBatch) -> TransactionBalances { pub fn collect_balances(&self, batch: &TransactionBatch) -> TransactionBalances {
let mut balances: TransactionBalances = vec![]; 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![]; let mut transaction_balances: Vec<u64> = vec![];
for account_key in transaction.message.account_keys.iter() { for account_key in transaction.message.account_keys.iter() {
transaction_balances.push(self.get_balance(account_key)); transaction_balances.push(self.get_balance(account_key));
@ -2882,30 +2853,25 @@ impl Bank {
let mut error_counters = ErrorCounters::default(); let mut error_counters = ErrorCounters::default();
let mut load_time = Measure::start("accounts_load"); let mut load_time = Measure::start("accounts_load");
let retryable_txs: Vec<_> = let retryable_txs: Vec<_> = batch
OrderedIterator::new(batch.lock_results(), batch.iteration_order()) .lock_results()
.iter()
.enumerate() .enumerate()
.filter_map(|(index, (_, res))| match res { .filter_map(|(index, res)| match res {
Err(TransactionError::AccountInUse) => { Err(TransactionError::AccountInUse) => {
error_counters.account_in_use += 1; error_counters.account_in_use += 1;
Some(index) Some(index)
} }
Ok(_) => None,
Err(_) => None, Err(_) => None,
Ok(_) => None,
}) })
.collect(); .collect();
let sig_results = self.check_transactions( let sig_results =
txs, self.check_transactions(txs, batch.lock_results(), max_age, &mut error_counters);
batch.iteration_order(),
batch.lock_results(),
max_age,
&mut error_counters,
);
let mut loaded_accounts = self.rc.accounts.load_accounts( let mut loaded_accounts = self.rc.accounts.load_accounts(
&self.ancestors, &self.ancestors,
txs, txs,
batch.iteration_order(),
sig_results, sig_results,
&self.blockhash_queue.read().unwrap(), &self.blockhash_queue.read().unwrap(),
&mut error_counters, &mut error_counters,
@ -2925,8 +2891,8 @@ impl Bank {
let executed: Vec<TransactionExecutionResult> = loaded_accounts let executed: Vec<TransactionExecutionResult> = loaded_accounts
.iter_mut() .iter_mut()
.zip(OrderedIterator::new(txs, batch.iteration_order())) .zip(txs)
.map(|(accs, (_, tx))| match accs { .map(|(accs, tx)| match accs {
(Err(e), _nonce_rollback) => (Err(e.clone()), None), (Err(e), _nonce_rollback) => (Err(e.clone()), None),
(Ok(loaded_transaction), nonce_rollback) => { (Ok(loaded_transaction), nonce_rollback) => {
signature_count += u64::from(tx.message().header.num_required_signatures); signature_count += u64::from(tx.message().header.num_required_signatures);
@ -3025,11 +2991,7 @@ impl Bank {
let transaction_log_collector_config = let transaction_log_collector_config =
self.transaction_log_collector_config.read().unwrap(); self.transaction_log_collector_config.read().unwrap();
for (i, ((r, _nonce_rollback), (_, tx))) in executed for (i, ((r, _nonce_rollback), tx)) in executed.iter().zip(txs).enumerate() {
.iter()
.zip(OrderedIterator::new(txs, batch.iteration_order()))
.enumerate()
{
if let Some(debug_keys) = &self.transaction_debug_keys { if let Some(debug_keys) = &self.transaction_debug_keys {
for key in &tx.message.account_keys { for key in &tx.message.account_keys {
if debug_keys.contains(key) { if debug_keys.contains(key) {
@ -3112,7 +3074,6 @@ impl Bank {
fn filter_program_errors_and_collect_fee( fn filter_program_errors_and_collect_fee(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
iteration_order: Option<&[usize]>,
executed: &[TransactionExecutionResult], executed: &[TransactionExecutionResult],
) -> Vec<Result<()>> { ) -> Vec<Result<()>> {
let hash_queue = self.blockhash_queue.read().unwrap(); let hash_queue = self.blockhash_queue.read().unwrap();
@ -3122,9 +3083,10 @@ impl Bank {
secp256k1_program_enabled: self.secp256k1_program_enabled(), secp256k1_program_enabled: self.secp256k1_program_enabled(),
}; };
let results = OrderedIterator::new(txs, iteration_order) let results = txs
.zip(executed.iter()) .iter()
.map(|((_, tx), (res, nonce_rollback))| { .zip(executed)
.map(|(tx, (res, nonce_rollback))| {
let (fee_calculator, is_durable_nonce) = nonce_rollback let (fee_calculator, is_durable_nonce) = nonce_rollback
.as_ref() .as_ref()
.map(|nonce_rollback| nonce_rollback.fee_calculator()) .map(|nonce_rollback| nonce_rollback.fee_calculator())
@ -3172,7 +3134,6 @@ impl Bank {
pub fn commit_transactions( pub fn commit_transactions(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
iteration_order: Option<&[usize]>,
loaded_accounts: &mut [TransactionLoadResult], loaded_accounts: &mut [TransactionLoadResult],
executed: &[TransactionExecutionResult], executed: &[TransactionExecutionResult],
tx_count: u64, tx_count: u64,
@ -3211,7 +3172,6 @@ impl Bank {
self.rc.accounts.store_cached( self.rc.accounts.store_cached(
self.slot(), self.slot(),
txs, txs,
iteration_order,
executed, executed,
loaded_accounts, loaded_accounts,
&self.rent_collector, &self.rent_collector,
@ -3221,16 +3181,14 @@ impl Bank {
); );
self.collect_rent(executed, loaded_accounts); self.collect_rent(executed, loaded_accounts);
let overwritten_vote_accounts = let overwritten_vote_accounts = self.update_cached_accounts(txs, executed, loaded_accounts);
self.update_cached_accounts(txs, iteration_order, executed, loaded_accounts);
// once committed there is no way to unroll // once committed there is no way to unroll
write_time.stop(); write_time.stop();
debug!("store: {}us txs_len={}", write_time.as_us(), txs.len(),); debug!("store: {}us txs_len={}", write_time.as_us(), txs.len(),);
timings.store_us += write_time.as_us(); timings.store_us += write_time.as_us();
self.update_transaction_statuses(txs, iteration_order, &executed); self.update_transaction_statuses(txs, &executed);
let fee_collection_results = let fee_collection_results = self.filter_program_errors_and_collect_fee(txs, executed);
self.filter_program_errors_and_collect_fee(txs, iteration_order, executed);
TransactionResults { TransactionResults {
fee_collection_results, fee_collection_results,
@ -3830,7 +3788,6 @@ impl Bank {
let results = self.commit_transactions( let results = self.commit_transactions(
batch.transactions(), batch.transactions(),
batch.iteration_order(),
&mut loaded_accounts, &mut loaded_accounts,
&executed, &executed,
tx_count, tx_count,
@ -3852,7 +3809,7 @@ impl Bank {
#[must_use] #[must_use]
pub fn process_transactions(&self, txs: &[Transaction]) -> Vec<Result<()>> { 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( self.load_execute_and_commit_transactions(
&batch, &batch,
MAX_PROCESSING_AGE, MAX_PROCESSING_AGE,
@ -4421,16 +4378,11 @@ impl Bank {
fn update_cached_accounts( fn update_cached_accounts(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
iteration_order: Option<&[usize]>,
res: &[TransactionExecutionResult], res: &[TransactionExecutionResult],
loaded: &[TransactionLoadResult], loaded: &[TransactionLoadResult],
) -> Vec<OverwrittenVoteAccount> { ) -> Vec<OverwrittenVoteAccount> {
let mut overwritten_vote_accounts = vec![]; let mut overwritten_vote_accounts = vec![];
for (i, ((raccs, _load_nonce_rollback), (transaction_index, tx))) in loaded for (i, ((raccs, _load_nonce_rollback), tx)) in loaded.iter().zip(txs).enumerate() {
.iter()
.zip(OrderedIterator::new(txs, iteration_order))
.enumerate()
{
let (res, _res_nonce_rollback) = &res[i]; let (res, _res_nonce_rollback) = &res[i];
if res.is_err() || raccs.is_err() { if res.is_err() || raccs.is_err() {
continue; continue;
@ -4452,9 +4404,10 @@ impl Bank {
self.stake_program_v2_enabled(), self.stake_program_v2_enabled(),
self.check_init_vote_data_enabled(), self.check_init_vote_data_enabled(),
) { ) {
// TODO: one of the indices is redundant.
overwritten_vote_accounts.push(OverwrittenVoteAccount { overwritten_vote_accounts.push(OverwrittenVoteAccount {
account: old_vote_account, account: old_vote_account,
transaction_index, transaction_index: i,
transaction_result_index: i, transaction_result_index: i,
}); });
} }
@ -7600,7 +7553,7 @@ pub(crate) mod tests {
]; ];
let initial_balance = bank.get_balance(&leader); 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(); bank.freeze();
assert_eq!( assert_eq!(
bank.get_balance(&leader), bank.get_balance(&leader),
@ -7726,7 +7679,7 @@ pub(crate) mod tests {
system_transaction::transfer(&mint_keypair, &alice.pubkey(), 1, genesis_config.hash()); system_transaction::transfer(&mint_keypair, &alice.pubkey(), 1, genesis_config.hash());
let pay_alice = vec![tx1]; 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 let results_alice = bank
.load_execute_and_commit_transactions( .load_execute_and_commit_transactions(
&lock_result, &lock_result,
@ -7779,7 +7732,7 @@ pub(crate) mod tests {
let tx = Transaction::new(&[&key0], message, genesis_config.hash()); let tx = Transaction::new(&[&key0], message, genesis_config.hash());
let txs = vec![tx]; let txs = vec![tx];
let batch0 = bank.prepare_batch(&txs, None); let batch0 = bank.prepare_batch(&txs);
assert!(batch0.lock_results()[0].is_ok()); assert!(batch0.lock_results()[0].is_ok());
// Try locking accounts, locking a previously read-only account as writable // 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 tx = Transaction::new(&[&key1], message, genesis_config.hash());
let txs = vec![tx]; let txs = vec![tx];
let batch1 = bank.prepare_batch(&txs, None); let batch1 = bank.prepare_batch(&txs);
assert!(batch1.lock_results()[0].is_err()); assert!(batch1.lock_results()[0].is_err());
// Try locking a previously read-only account a 2nd time; should succeed // 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 tx = Transaction::new(&[&key2], message, genesis_config.hash());
let txs = vec![tx]; let txs = vec![tx];
let batch2 = bank.prepare_batch(&txs, None); let batch2 = bank.prepare_batch(&txs);
assert!(batch2.lock_results()[0].is_ok()); assert!(batch2.lock_results()[0].is_ok());
} }
@ -9650,15 +9603,14 @@ pub(crate) mod tests {
instructions, instructions,
); );
let txs = vec![tx0, tx1]; let txs = vec![tx0, tx1];
let iteration_order: Vec<usize> = vec![0, 1]; let batch = bank0.prepare_batch(&txs);
let batch = bank0.prepare_batch(&txs, Some(iteration_order));
let balances = bank0.collect_balances(&batch); let balances = bank0.collect_balances(&batch);
assert_eq!(balances.len(), 2); assert_eq!(balances.len(), 2);
assert_eq!(balances[0], vec![8, 11, 1]); assert_eq!(balances[0], vec![8, 11, 1]);
assert_eq!(balances[1], vec![8, 0, 1]); assert_eq!(balances[1], vec![8, 0, 1]);
let iteration_order: Vec<usize> = vec![1, 0]; let txs: Vec<_> = txs.iter().rev().cloned().collect();
let batch = bank0.prepare_batch(&txs, Some(iteration_order)); let batch = bank0.prepare_batch(&txs);
let balances = bank0.collect_balances(&batch); let balances = bank0.collect_balances(&batch);
assert_eq!(balances.len(), 2); assert_eq!(balances.len(), 2);
assert_eq!(balances[0], vec![8, 0, 1]); 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 tx2 = system_transaction::transfer(&keypair1, &pubkey2, 12, blockhash);
let txs = vec![tx0, tx1, tx2]; 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) = let (transaction_results, transaction_balances_set, inner_instructions, transaction_logs) =
bank0.load_execute_and_commit_transactions( bank0.load_execute_and_commit_transactions(
&lock_result, &lock_result,
@ -12126,10 +12078,8 @@ pub(crate) mod tests {
let success_sig = tx0.signatures[0]; let success_sig = tx0.signatures[0];
let tx1 = system_transaction::transfer(&sender1, &recipient1, 110, blockhash); // Should produce insufficient funds log let tx1 = system_transaction::transfer(&sender1, &recipient1, 110, blockhash); // Should produce insufficient funds log
let failure_sig = tx1.signatures[0]; let failure_sig = tx1.signatures[0];
let txs = vec![tx0, tx1]; let txs = vec![tx1, tx0];
let batch = bank.prepare_batch(&txs);
// Use non-sequential batch ordering
let batch = bank.prepare_batch(&txs, Some(vec![1, 0]));
let log_results = bank let log_results = bank
.load_execute_and_commit_transactions( .load_execute_and_commit_transactions(

View File

@ -34,7 +34,6 @@ pub mod stakes;
pub mod status_cache; pub mod status_cache;
mod system_instruction_processor; mod system_instruction_processor;
pub mod transaction_batch; pub mod transaction_batch;
pub mod transaction_utils;
pub mod vote_account; pub mod vote_account;
pub mod vote_sender_types; pub mod vote_sender_types;

View File

@ -6,7 +6,6 @@ pub struct TransactionBatch<'a, 'b> {
lock_results: Vec<Result<()>>, lock_results: Vec<Result<()>>,
bank: &'a Bank, bank: &'a Bank,
transactions: &'b [Transaction], transactions: &'b [Transaction],
iteration_order: Option<Vec<usize>>,
pub(crate) needs_unlock: bool, pub(crate) needs_unlock: bool,
} }
@ -15,17 +14,12 @@ impl<'a, 'b> TransactionBatch<'a, 'b> {
lock_results: Vec<Result<()>>, lock_results: Vec<Result<()>>,
bank: &'a Bank, bank: &'a Bank,
transactions: &'b [Transaction], transactions: &'b [Transaction],
iteration_order: Option<Vec<usize>>,
) -> Self { ) -> Self {
assert_eq!(lock_results.len(), transactions.len()); assert_eq!(lock_results.len(), transactions.len());
if let Some(iteration_order) = &iteration_order {
assert_eq!(transactions.len(), iteration_order.len());
}
Self { Self {
lock_results, lock_results,
bank, bank,
transactions, transactions,
iteration_order,
needs_unlock: true, needs_unlock: true,
} }
} }
@ -38,14 +32,6 @@ impl<'a, 'b> TransactionBatch<'a, 'b> {
self.transactions 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 { pub fn bank(&self) -> &Bank {
self.bank self.bank
} }
@ -69,20 +55,20 @@ mod tests {
let (bank, txs) = setup(); let (bank, txs) = setup();
// Test getting locked accounts // Test getting locked accounts
let batch = bank.prepare_batch(&txs, None); let batch = bank.prepare_batch(&txs);
// Grab locks // Grab locks
assert!(batch.lock_results().iter().all(|x| x.is_ok())); assert!(batch.lock_results().iter().all(|x| x.is_ok()));
// Trying to grab locks again should fail // 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())); assert!(batch2.lock_results().iter().all(|x| x.is_err()));
// Drop the first set of locks // Drop the first set of locks
drop(batch); drop(batch);
// Now grabbing locks should work again // 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())); 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())); assert!(batch.lock_results().iter().all(|x| x.is_ok()));
// Grab locks // 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())); assert!(batch2.lock_results().iter().all(|x| x.is_ok()));
// Prepare another batch without locks // 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::{ use solana_account_decoder::parse_token::{
spl_token_id_v2_0, spl_token_v2_0_native_mint, token_amount_to_ui_amount, UiTokenAmount, spl_token_id_v2_0, spl_token_v2_0_native_mint, token_amount_to_ui_amount, UiTokenAmount,
}; };
use solana_runtime::{ use solana_runtime::{bank::Bank, transaction_batch::TransactionBatch};
bank::Bank, transaction_batch::TransactionBatch, transaction_utils::OrderedIterator,
};
use solana_sdk::{account::ReadableAccount, pubkey::Pubkey}; use solana_sdk::{account::ReadableAccount, pubkey::Pubkey};
use spl_token_v2_0::{ use spl_token_v2_0::{
solana_program::program_pack::Pack, solana_program::program_pack::Pack,
@ -57,7 +55,7 @@ pub fn collect_token_balances(
) -> TransactionTokenBalances { ) -> TransactionTokenBalances {
let mut balances: TransactionTokenBalances = vec![]; 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 account_keys = &transaction.message.account_keys;
let has_token_program = account_keys.iter().any(|p| is_token_program(p)); let has_token_program = account_keys.iter().any(|p| is_token_program(p));