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:
@ -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();
|
||||
});
|
||||
}
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user