Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add withdraw durable nonce #26829

Merged
merged 2 commits into from
Jul 30, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bench-tps/src/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -924,5 +924,6 @@ mod tests {
rent
);
}
withdraw_durable_nonce_accounts(client, &authority_keypairs, &nonce_keypairs)
}
}
179 changes: 125 additions & 54 deletions bench-tps/src/send_batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,35 @@ pub fn generate_durable_nonce_accounts<T: 'static + BenchTpsClient + Send + Sync
let (mut nonce_keypairs, _extra) = generate_keypairs(seed_keypair, count as u64);
nonce_keypairs.truncate(count);

let to_fund: Vec<(&Keypair, &Keypair)> = authority_keypairs
let to_fund: Vec<NonceCreateSigners> = authority_keypairs
Copy link
Contributor Author

@KirillLykov KirillLykov Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replace pair to struct in order to have two different types for Withdraw and Create accounts. It is required because for Create I need two signatures and for Withdraw only one (Sliceable::to_slice method returns array of signing Keypairs).

.iter()
.zip(nonce_keypairs.iter())
.map(|x| NonceCreateSigners(x.0, x.1))
.collect();

to_fund.chunks(FUND_CHUNK_LEN).for_each(|chunk| {
NonceContainer::with_capacity(chunk.len()).create_accounts(&client, chunk, nonce_rent);
NonceCreateContainer::with_capacity(chunk.len())
.create_accounts(&client, chunk, nonce_rent);
});
nonce_keypairs
}

pub fn withdraw_durable_nonce_accounts<T: 'static + BenchTpsClient + Send + Sync>(
client: Arc<T>,
authority_keypairs: &[Keypair],
nonce_keypairs: &[Keypair],
) {
let to_withdraw: Vec<NonceWithdrawSigners> = authority_keypairs
.iter()
.zip(nonce_keypairs.iter())
.map(|x| NonceWithdrawSigners(x.0, x.1))
.collect();

to_withdraw.chunks(FUND_CHUNK_LEN).for_each(|chunk| {
NonceWithdrawContainer::with_capacity(chunk.len()).withdraw_accounts(&client, chunk);
});
}

const MAX_SPENDS_PER_TX: u64 = 4;

// Size of the chunk of transactions
Expand All @@ -144,6 +162,11 @@ fn verify_funding_transfer<T: BenchTpsClient>(
/// Helper trait to encapsulate common logic for sending transactions batch
///
trait SendBatchTransactions<'a, T: Sliceable + Send + Sync> {
fn make<V: Send + Sync, F: Fn(&V) -> (T, Transaction) + Send + Sync>(
&mut self,
chunk: &[V],
create_transaction: F,
);
fn send_transactions<C, F>(&mut self, client: &Arc<C>, to_lamports: u64, log_progress: F)
where
C: 'static + BenchTpsClient + Send + Sync,
Expand All @@ -170,6 +193,18 @@ impl<'a, T: Sliceable + Send + Sync> SendBatchTransactions<'a, T> for Vec<(T, Tr
where
<T as Sliceable>::Slice: Signers,
{
fn make<V: Send + Sync, F: Fn(&V) -> (T, Transaction) + Send + Sync>(
&mut self,
chunk: &[V],
create_transaction: F,
) {
let mut make_txs = Measure::start("make_txs");
let txs: Vec<(T, Transaction)> = chunk.par_iter().map(create_transaction).collect();
make_txs.stop();
debug!("make {} unsigned txs: {}us", txs.len(), make_txs.as_us());
self.extend(txs);
}

fn send_transactions<C, F>(&mut self, client: &Arc<C>, to_lamports: u64, log_progress: F)
where
C: 'static + BenchTpsClient + Send + Sync,
Expand Down Expand Up @@ -312,7 +347,6 @@ trait FundingTransactions<'a>: SendBatchTransactions<'a, FundingSigners<'a>> {
to_fund: &FundingChunk<'a>,
to_lamports: u64,
);
fn make(&mut self, to_fund: &FundingChunk<'a>);
}

impl<'a> FundingTransactions<'a> for FundingContainer<'a> {
Expand All @@ -322,7 +356,11 @@ impl<'a> FundingTransactions<'a> for FundingContainer<'a> {
to_fund: &FundingChunk<'a>,
to_lamports: u64,
) {
self.make(to_fund);
self.make(to_fund, |(k, t)| -> (FundingSigners<'a>, Transaction) {
let instructions = system_instruction::transfer_many(&k.pubkey(), t);
let message = Message::new(&instructions, Some(&k.pubkey()));
(*k, Transaction::new_unsigned(message))
});

let log_progress = |tries: usize, batch_len: usize| {
info!(
Expand All @@ -339,28 +377,15 @@ impl<'a> FundingTransactions<'a> for FundingContainer<'a> {
};
self.send_transactions(client, to_lamports, log_progress);
}

fn make(&mut self, to_fund: &FundingChunk<'a>) {
let mut make_txs = Measure::start("make_txs");
let to_fund_txs: FundingContainer<'a> = to_fund
.par_iter()
.map(|(k, t)| {
let instructions = system_instruction::transfer_many(&k.pubkey(), t);
let message = Message::new(&instructions, Some(&k.pubkey()));
(*k, Transaction::new_unsigned(message))
})
.collect();
make_txs.stop();
debug!(
"make {} unsigned txs: {}us",
to_fund_txs.len(),
make_txs.as_us()
);
self.extend(to_fund_txs);
}
}

impl<'a> Sliceable for (&'a Keypair, &'a Keypair) {
// Introduce a new structure to specify Sliceable implementations
// which uses both Keypairs to sign the transaction
struct NonceCreateSigners<'a>(&'a Keypair, &'a Keypair);
type NonceCreateChunk<'a> = [NonceCreateSigners<'a>];
type NonceCreateContainer<'a> = Vec<(NonceCreateSigners<'a>, Transaction)>;

impl<'a> Sliceable for NonceCreateSigners<'a> {
type Slice = [&'a Keypair; 2];
fn as_slice(&self) -> Self::Slice {
[self.0, self.1]
Expand All @@ -370,28 +395,36 @@ impl<'a> Sliceable for (&'a Keypair, &'a Keypair) {
}
}

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)> {
trait NonceTransactions<'a>: SendBatchTransactions<'a, NonceCreateSigners<'a>> {
fn create_accounts<T: 'static + BenchTpsClient + Send + Sync>(
&mut self,
client: &Arc<T>,
to_fund: &'a NonceChunk<'a>,
to_fund: &'a NonceCreateChunk<'a>,
nonce_rent: u64,
);
fn make(&mut self, nonce_rent: u64, to_fund: &'a NonceChunk<'a>);
}

impl<'a> CreateNonceTransactions<'a> for NonceContainer<'a> {
impl<'a> NonceTransactions<'a> for NonceCreateContainer<'a> {
fn create_accounts<T: 'static + BenchTpsClient + Send + Sync>(
&mut self,
client: &Arc<T>,
to_fund: &'a NonceChunk<'a>,
to_fund: &'a NonceCreateChunk<'a>,
nonce_rent: u64,
) {
self.make(nonce_rent, to_fund);
self.make(to_fund, |kp| -> (NonceCreateSigners<'a>, Transaction) {
let authority = kp.0;
let nonce: &Keypair = kp.1;
let instructions = system_instruction::create_nonce_account(
&authority.pubkey(),
&nonce.pubkey(),
&authority.pubkey(),
nonce_rent,
);
(
NonceCreateSigners(authority, nonce),
Transaction::new_with_payer(&instructions, Some(&authority.pubkey())),
)
});

let log_progress = |tries: usize, batch_len: usize| {
info!(
Expand All @@ -402,30 +435,68 @@ impl<'a> CreateNonceTransactions<'a> for NonceContainer<'a> {
};
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,
);
struct NonceWithdrawSigners<'a>(&'a Keypair, &'a Keypair);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It would be clearer for the second parameter to be a Pubkey since it's not signing, but I think that'll make things more complicated in the implementation, like (&'a Keypair, Pubkey), which is probably worse. I imagine you already considered this tradeoff 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this is really good point! And it happens that it is quite easy to implement

type NonceWithdrawChunk<'a> = [NonceWithdrawSigners<'a>];
type NonceWithdrawContainer<'a> = Vec<(NonceWithdrawSigners<'a>, Transaction)>;

impl<'a> Sliceable for NonceWithdrawSigners<'a> {
type Slice = [&'a Keypair; 1];
fn as_slice(&self) -> Self::Slice {
[self.0]
}
fn get_pubkey(&self) -> Pubkey {
self.0.pubkey()
}
}

trait NonceWithdrawTransactions<'a>: SendBatchTransactions<'a, NonceWithdrawSigners<'a>> {
fn withdraw_accounts<T: 'static + BenchTpsClient + Send + Sync>(
&mut self,
client: &Arc<T>,
to_withdraw: &'a NonceWithdrawChunk<'a>,
);
}
impl<'a> NonceWithdrawTransactions<'a> for NonceWithdrawContainer<'a> {
fn withdraw_accounts<T: 'static + BenchTpsClient + Send + Sync>(
&mut self,
client: &Arc<T>,
to_withdraw: &'a NonceWithdrawChunk<'a>,
) {
self.make(
to_withdraw,
|kp| -> (NonceWithdrawSigners<'a>, Transaction) {
let authority = kp.0;
let nonce: &Keypair = kp.1;
let nonce_balance = client.get_balance(&nonce.pubkey()).unwrap();
let instructions = vec![
system_instruction::withdraw_nonce_account(
&nonce.pubkey(),
&authority.pubkey(),
&authority.pubkey(),
nonce_balance,
);
1
];
(
(*authority, *nonce),
NonceWithdrawSigners(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);

let log_progress = |tries: usize, batch_len: usize| {
info!(
"@ {} {} accounts",
if tries == 0 {
"withdrawing"
} else {
" retrying"
},
batch_len,
);
};
self.send_transactions(client, 0, log_progress);
}
}