add create durable nonce accounts (#26513)

* add create durable nonce accounts

* extract common logic into method

* add test

* got rid of unnecessary Result
This commit is contained in:
kirill lykov 2022-07-18 13:38:20 +02:00 committed by GitHub
parent e69d8ae7d7
commit c4ec031daa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 160 additions and 23 deletions

View File

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

View File

@ -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<T: 'static + BenchTpsClient + Send + Sync>(
}
}
pub fn generate_durable_nonce_accounts<T: 'static + BenchTpsClient + Send + Sync>(
client: Arc<T>,
authority_keypairs: &[Keypair],
) -> Vec<Keypair> {
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<T: BenchTpsClient>(
false
}
/// Helper trait to encapsulate common logic for sending transactions batch
///
trait SendBatchTransactions<'a, T: Sliceable + Send + Sync> {
fn send_transactions<C, F>(&mut self, client: &Arc<C>, to_lamports: u64, log_progress: F)
where
C: 'static + BenchTpsClient + Send + Sync,
F: Fn(usize, usize);
fn sign(&mut self, blockhash: Hash);
fn send<C: BenchTpsClient>(&self, client: &Arc<C>);
fn verify<C: 'static + BenchTpsClient + Send + Sync>(
@ -139,6 +170,33 @@ impl<'a, T: Sliceable + Send + Sync> SendBatchTransactions<'a, T> for Vec<(T, Tr
where
<T as Sliceable>::Slice: Signers,
{
fn send_transactions<C, F>(&mut self, client: &Arc<C>, 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<T: 'static + BenchTpsClient + Send + Sync>(
&mut self,
client: &Arc<T>,
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<T: 'static + BenchTpsClient + Send + Sync>(
&mut self,
client: &Arc<T>,
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);
}
}