From 754f25ae99bfdbd97beab44b884bca12272c34f3 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Fri, 5 Jun 2020 10:06:01 +0800 Subject: [PATCH] Avoid AccountInUse errors when simulating transactions (#10391) automerge --- core/src/rpc.rs | 12 +++++------- runtime/src/bank.rs | 9 +++++++++ runtime/src/transaction_batch.rs | 17 +++++++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/core/src/rpc.rs b/core/src/rpc.rs index a76cbe8635..f303e26a75 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -718,13 +718,11 @@ fn verify_signature(input: &str) -> Result { } /// Run transactions against a frozen bank without committing the results -fn run_transaction_simulation( - bank: &Bank, - transactions: &[Transaction], -) -> transaction::Result<()> { +fn run_transaction_simulation(bank: &Bank, transaction: Transaction) -> transaction::Result<()> { assert!(bank.is_frozen(), "simulation bank must be frozen"); - let batch = bank.prepare_batch(transactions, None); + let txs = &[transaction]; + let batch = bank.prepare_simulation_batch(txs); let (_loaded_accounts, executed, _retryable_transactions, _transaction_count, _signature_count) = bank.load_and_execute_transactions(&batch, solana_sdk::clock::MAX_PROCESSING_AGE); executed[0].0.clone().map(|_| ()) @@ -1454,7 +1452,7 @@ impl RpcSol for RpcSolImpl { } let bank = &*meta.request_processor.read().unwrap().bank(None)?; - if let Err(err) = run_transaction_simulation(&bank, &[transaction]) { + if let Err(err) = run_transaction_simulation(&bank, transaction) { // Note: it's possible that the transaction simulation failed but the actual // transaction would succeed, such as when a transaction depends on an earlier // transaction that has yet to reach max confirmations. In these cases the user @@ -1501,7 +1499,7 @@ impl RpcSol for RpcSolImpl { let bank = &*meta.request_processor.read().unwrap().bank(None)?; if result.is_ok() { - result = run_transaction_simulation(&bank, &[transaction]); + result = run_transaction_simulation(&bank, transaction); } new_response( diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 8959f236ac..b1515adb82 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1058,6 +1058,15 @@ impl Bank { TransactionBatch::new(results, &self, txs, iteration_order) } + pub fn prepare_simulation_batch<'a, 'b>( + &'a self, + txs: &'b [Transaction], + ) -> TransactionBatch<'a, 'b> { + let mut batch = TransactionBatch::new(vec![Ok(()); txs.len()], &self, txs, None); + batch.needs_unlock = false; + batch + } + pub fn unlock_accounts(&self, batch: &mut TransactionBatch) { if batch.needs_unlock { batch.needs_unlock = false; diff --git a/runtime/src/transaction_batch.rs b/runtime/src/transaction_batch.rs index 597de36274..9f03ad33fa 100644 --- a/runtime/src/transaction_batch.rs +++ b/runtime/src/transaction_batch.rs @@ -81,6 +81,23 @@ mod tests { assert!(batch2.lock_results().iter().all(|x| x.is_ok())); } + #[test] + fn test_simulation_batch() { + let (bank, txs) = setup(); + + // Prepare batch without locks + let batch = bank.prepare_simulation_batch(&txs); + assert!(batch.lock_results().iter().all(|x| x.is_ok())); + + // Grab locks + let batch2 = bank.prepare_batch(&txs, None); + assert!(batch2.lock_results().iter().all(|x| x.is_ok())); + + // Prepare another batch without locks + let batch3 = bank.prepare_simulation_batch(&txs); + assert!(batch3.lock_results().iter().all(|x| x.is_ok())); + } + fn setup() -> (Bank, Vec) { let dummy_leader_pubkey = Pubkey::new_rand(); let GenesisConfigInfo {