From c4ec031daaa9c6650d37ec351fa680d1c92320be Mon Sep 17 00:00:00 2001 From: kirill lykov Date: Mon, 18 Jul 2022 13:38:20 +0200 Subject: [PATCH] add create durable nonce accounts (#26513) * add create durable nonce accounts * extract common logic into method * add test * got rid of unnecessary Result --- bench-tps/src/bench.rs | 28 ++++++- bench-tps/src/send_batch.rs | 155 +++++++++++++++++++++++++++++++----- 2 files changed, 160 insertions(+), 23 deletions(-) diff --git a/bench-tps/src/bench.rs b/bench-tps/src/bench.rs index 1dda75c216..fdf2d30cfc 100644 --- a/bench-tps/src/bench.rs +++ b/bench-tps/src/bench.rs @@ -710,7 +710,7 @@ mod tests { solana_runtime::{bank::Bank, bank_client::BankClient}, solana_sdk::{ commitment_config::CommitmentConfig, fee_calculator::FeeRateGovernor, - genesis_config::create_genesis_config, native_token::sol_to_lamports, + genesis_config::create_genesis_config, native_token::sol_to_lamports, nonce::State, }, }; @@ -774,4 +774,30 @@ mod tests { assert_eq!(client.get_balance(&kp.pubkey()).unwrap(), lamports + rent); } } + + #[test] + fn test_bench_tps_create_durable_nonce() { + let (genesis_config, id) = create_genesis_config(sol_to_lamports(10_000.0)); + let bank = Bank::new_for_tests(&genesis_config); + let client = Arc::new(BankClient::new(bank)); + let keypair_count = 10; + let lamports = 10_000_000; + + let authority_keypairs = + generate_and_fund_keypairs(client.clone(), &id, keypair_count, lamports).unwrap(); + + let nonce_keypairs = generate_durable_nonce_accounts(client.clone(), &authority_keypairs); + + let rent = client + .get_minimum_balance_for_rent_exemption(State::size()) + .unwrap(); + for kp in &nonce_keypairs { + assert_eq!( + client + .get_balance_with_commitment(&kp.pubkey(), CommitmentConfig::processed()) + .unwrap(), + rent + ); + } + } } diff --git a/bench-tps/src/send_batch.rs b/bench-tps/src/send_batch.rs index 250e5e2fe9..7487d17bcb 100644 --- a/bench-tps/src/send_batch.rs +++ b/bench-tps/src/send_batch.rs @@ -8,6 +8,7 @@ use { commitment_config::CommitmentConfig, hash::Hash, message::Message, + nonce::State, pubkey::Pubkey, signature::{Keypair, Signer}, signer::signers::Signers, @@ -95,6 +96,30 @@ pub fn fund_keys( } } +pub fn generate_durable_nonce_accounts( + client: Arc, + authority_keypairs: &[Keypair], +) -> Vec { + let nonce_rent = client + .get_minimum_balance_for_rent_exemption(State::size()) + .unwrap(); + + let seed_keypair = &authority_keypairs[0]; + let count = authority_keypairs.len(); + let (mut nonce_keypairs, _extra) = generate_keypairs(seed_keypair, count as u64); + nonce_keypairs.truncate(count); + + let to_fund: Vec<(&Keypair, &Keypair)> = authority_keypairs + .iter() + .zip(nonce_keypairs.iter()) + .collect(); + + to_fund.chunks(FUND_CHUNK_LEN).for_each(|chunk| { + NonceContainer::with_capacity(chunk.len()).create_accounts(&client, chunk, nonce_rent); + }); + nonce_keypairs +} + const MAX_SPENDS_PER_TX: u64 = 4; // Size of the chunk of transactions @@ -116,7 +141,13 @@ fn verify_funding_transfer( false } +/// Helper trait to encapsulate common logic for sending transactions batch +/// trait SendBatchTransactions<'a, T: Sliceable + Send + Sync> { + fn send_transactions(&mut self, client: &Arc, to_lamports: u64, log_progress: F) + where + C: 'static + BenchTpsClient + Send + Sync, + F: Fn(usize, usize); fn sign(&mut self, blockhash: Hash); fn send(&self, client: &Arc); fn verify( @@ -139,6 +170,33 @@ impl<'a, T: Sliceable + Send + Sync> SendBatchTransactions<'a, T> for Vec<(T, Tr where ::Slice: Signers, { + fn send_transactions(&mut self, client: &Arc, to_lamports: u64, log_progress: F) + where + C: 'static + BenchTpsClient + Send + Sync, + F: Fn(usize, usize), + { + let mut tries: usize = 0; + while !self.is_empty() { + log_progress(tries, self.len()); + let blockhash = get_latest_blockhash(client.as_ref()); + + // re-sign retained to_fund_txes with updated blockhash + self.sign(blockhash); + self.send(client); + + // Sleep a few slots to allow transactions to process + sleep(Duration::from_secs(1)); + + self.verify(client, to_lamports); + + // retry anything that seems to have dropped through cracks + // again since these txs are all or nothing, they're fine to + // retry + tries += 1; + } + info!("transactions sent in {} tries", tries); + } + fn sign(&mut self, blockhash: Hash) { let mut sign_txs = Measure::start("sign_txs"); self.par_iter_mut().for_each(|(k, tx)| { @@ -266,8 +324,7 @@ impl<'a> FundingTransactions<'a> for FundingContainer<'a> { ) { self.make(to_fund); - let mut tries = 0; - while !self.is_empty() { + let log_progress = |tries: usize, batch_len: usize| { info!( "{} {} each to {} accounts in {} txs", if tries == 0 { @@ -276,27 +333,11 @@ impl<'a> FundingTransactions<'a> for FundingContainer<'a> { " retrying" }, to_lamports, - self.len() * MAX_SPENDS_PER_TX as usize, - self.len(), + batch_len * MAX_SPENDS_PER_TX as usize, + batch_len, ); - - let blockhash = get_latest_blockhash(client.as_ref()); - - // re-sign retained to_fund_txes with updated blockhash - self.sign(blockhash); - self.send(client); - - // Sleep a few slots to allow transactions to process - sleep(Duration::from_secs(1)); - - self.verify(client, to_lamports); - - // retry anything that seems to have dropped through cracks - // again since these txs are all or nothing, they're fine to - // retry - tries += 1; - } - info!("transferred"); + }; + self.send_transactions(client, to_lamports, log_progress); } fn make(&mut self, to_fund: &FundingChunk<'a>) { @@ -318,3 +359,73 @@ impl<'a> FundingTransactions<'a> for FundingContainer<'a> { self.extend(to_fund_txs); } } + +impl<'a> Sliceable for (&'a Keypair, &'a Keypair) { + type Slice = [&'a Keypair; 2]; + fn as_slice(&self) -> Self::Slice { + [self.0, self.1] + } + fn get_pubkey(&self) -> Pubkey { + self.0.pubkey() + } +} + +type NonceSigners<'a> = (&'a Keypair, &'a Keypair); +type NonceChunk<'a> = [NonceSigners<'a>]; +type NonceContainer<'a> = Vec<(NonceSigners<'a>, Transaction)>; + +trait CreateNonceTransactions<'a>: SendBatchTransactions<'a, (&'a Keypair, &'a Keypair)> { + fn create_accounts( + &mut self, + client: &Arc, + to_fund: &'a NonceChunk<'a>, + nonce_rent: u64, + ); + fn make(&mut self, nonce_rent: u64, to_fund: &'a NonceChunk<'a>); +} + +impl<'a> CreateNonceTransactions<'a> for NonceContainer<'a> { + fn create_accounts( + &mut self, + client: &Arc, + to_fund: &'a NonceChunk<'a>, + nonce_rent: u64, + ) { + self.make(nonce_rent, to_fund); + + let log_progress = |tries: usize, batch_len: usize| { + info!( + "@ {} {} accounts", + if tries == 0 { "creating" } else { " retrying" }, + batch_len, + ); + }; + self.send_transactions(client, nonce_rent, log_progress); + } + + fn make(&mut self, nonce_rent: u64, to_fund: &'a NonceChunk<'a>) { + let mut make_txs = Measure::start("make_txs"); + let to_fund_txs: NonceContainer = to_fund + .par_iter() + .map(|(authority, nonce)| { + let instructions = system_instruction::create_nonce_account( + &authority.pubkey(), + &nonce.pubkey(), + &authority.pubkey(), + nonce_rent, + ); + ( + (*authority, *nonce), + Transaction::new_with_payer(&instructions, Some(&authority.pubkey())), + ) + }) + .collect(); + make_txs.stop(); + debug!( + "make {} unsigned txs: {}us", + to_fund_txs.len(), + make_txs.as_us() + ); + self.extend(to_fund_txs); + } +}