2
2
use {
3
3
agave_banking_stage_ingress_types:: BankingPacketBatch ,
4
4
assert_matches:: assert_matches,
5
- clap:: { crate_description, crate_name, Arg , ArgEnum , Command } ,
5
+ clap:: { crate_description, crate_name, Arg , Command } ,
6
6
crossbeam_channel:: { unbounded, Receiver } ,
7
7
log:: * ,
8
- rand:: { thread_rng, Rng } ,
8
+ rand:: { seq :: SliceRandom , thread_rng, Rng } ,
9
9
rayon:: prelude:: * ,
10
10
solana_core:: {
11
11
banking_stage:: { update_bank_forks_and_poh_recorder_for_new_tpu_bank, BankingStage } ,
@@ -79,28 +79,38 @@ fn check_txs(
79
79
no_bank
80
80
}
81
81
82
- #[ derive( ArgEnum , Clone , Copy , PartialEq , Eq ) ]
82
+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
83
83
enum WriteLockContention {
84
84
/// No transactions lock the same accounts.
85
85
None ,
86
86
/// Transactions don't lock the same account, unless they belong to the same batch.
87
87
SameBatchOnly ,
88
88
/// All transactions write lock the same account.
89
89
Full ,
90
+ /// Transactions randomly lock any two accounts from the pool of accounts, contention depends
91
+ /// on the number of accounts.
92
+ AccountContention ( i64 ) ,
90
93
}
91
94
92
- impl WriteLockContention {
93
- fn possible_values < ' a > ( ) -> impl Iterator < Item = clap:: PossibleValue < ' a > > {
94
- Self :: value_variants ( )
95
- . iter ( )
96
- . filter_map ( |v| v. to_possible_value ( ) )
97
- }
98
- }
99
-
100
- impl std:: str:: FromStr for WriteLockContention {
101
- type Err = String ;
102
- fn from_str ( input : & str ) -> Result < Self , String > {
103
- ArgEnum :: from_str ( input, false )
95
+ fn parse_write_lock_contention ( argument : & str ) -> Result < WriteLockContention , String > {
96
+ if let Some ( ( "account-contention" , value) ) = argument. split_once ( "=" ) {
97
+ match value. parse :: < i64 > ( ) {
98
+ Ok ( num_accts) if num_accts > 1 => Ok ( WriteLockContention :: AccountContention ( num_accts) ) ,
99
+ _ => Err ( "Account contention value must be an integer greater than 1" . to_string ( ) ) ,
100
+ }
101
+ } else {
102
+ match argument {
103
+ "none" => Ok ( WriteLockContention :: None ) ,
104
+ "same-batch-only" => Ok ( WriteLockContention :: SameBatchOnly ) ,
105
+ "full" => Ok ( WriteLockContention :: Full ) ,
106
+ _ => Err ( format ! (
107
+ "Invalid contention option. Valid options are:\n \
108
+ - none\n \
109
+ - same-batch-only\n \
110
+ - full\n \
111
+ - account-contention=N (where N is a positive integer)"
112
+ ) ) ,
113
+ }
104
114
}
105
115
}
106
116
@@ -112,47 +122,95 @@ fn make_accounts_txs(
112
122
simulate_mint : bool ,
113
123
mint_txs_percentage : usize ,
114
124
) -> Vec < Transaction > {
125
+ let payer_keypair = Keypair :: new ( ) ;
115
126
let to_pubkey = pubkey:: new_rand ( ) ;
116
- let chunk_pubkeys: Vec < pubkey:: Pubkey > = ( 0 ..total_num_transactions / packets_per_batch)
117
- . map ( |_| pubkey:: new_rand ( ) )
118
- . collect ( ) ;
119
- let payer_key = Keypair :: new ( ) ;
120
- ( 0 ..total_num_transactions)
121
- . into_par_iter ( )
122
- . map ( |i| {
123
- let is_simulated_mint = is_simulated_mint_transaction (
124
- simulate_mint,
125
- i,
126
- packets_per_batch,
127
- mint_txs_percentage,
128
- ) ;
129
- // simulated mint transactions have higher compute-unit-price
130
- let compute_unit_price = if is_simulated_mint { 5 } else { 1 } ;
131
- let mut new = make_transfer_transaction_with_compute_unit_price (
132
- & payer_key,
133
- & to_pubkey,
134
- 1 ,
135
- hash,
136
- compute_unit_price,
137
- ) ;
138
- let sig: [ u8 ; 64 ] = std:: array:: from_fn ( |_| thread_rng ( ) . gen :: < u8 > ( ) ) ;
139
- new. message . account_keys [ 0 ] = pubkey:: new_rand ( ) ;
140
- new. message . account_keys [ 1 ] = match contention {
141
- WriteLockContention :: None => pubkey:: new_rand ( ) ,
142
- WriteLockContention :: SameBatchOnly => {
143
- // simulated mint transactions have conflict accounts
144
- if is_simulated_mint {
145
- chunk_pubkeys[ i / packets_per_batch]
146
- } else {
147
- pubkey:: new_rand ( )
127
+
128
+ if let WriteLockContention :: AccountContention ( num_accts) = contention{
129
+ let accounts: Vec < Pubkey > = ( 0 ..num_accts)
130
+ . map ( |_| pubkey:: new_rand ( ) )
131
+ . collect ( ) ;
132
+
133
+ ( 0 ..total_num_transactions)
134
+ . into_par_iter ( )
135
+ . map ( |i| {
136
+ let mut rng = thread_rng ( ) ;
137
+ let from_pubkey = accounts. choose ( & mut rng) . unwrap ( ) ;
138
+ let to_pubkey = loop {
139
+ let candidate = accounts. choose ( & mut rng) . unwrap ( ) ;
140
+ if candidate != from_pubkey { break candidate}
141
+ } ;
142
+
143
+ // simulated mint transactions have higher compute unit price.
144
+ let is_simulated_mint = is_simulated_mint_transaction (
145
+ simulate_mint, i,
146
+ packets_per_batch,
147
+ mint_txs_percentage
148
+ ) ;
149
+
150
+ let compute_unit_price = if is_simulated_mint { 5 } else { 1 } ;
151
+
152
+ let lamports = ( i + 1 ) as u64 ; //ensure non zero and non-constant lamports transferred.
153
+
154
+ let mut new_tx = make_transfer_transaction_with_compute_unit_price (
155
+ & payer_keypair,
156
+ to_pubkey,
157
+ lamports,
158
+ hash,
159
+ compute_unit_price
160
+ ) ;
161
+
162
+ new_tx. message . account_keys [ 0 ] = * from_pubkey;
163
+ new_tx. message . account_keys [ 1 ] = * to_pubkey;
164
+
165
+ let sig: [ u8 ; 64 ] = std:: array:: from_fn ( |_| thread_rng ( ) . gen :: < u8 > ( ) ) ;
166
+ new_tx. signatures = vec ! [ Signature :: from( sig) ] ;
167
+ new_tx
168
+
169
+ } )
170
+ . collect ( )
171
+ } else {
172
+ let chunk_pubkeys: Vec < pubkey:: Pubkey > = ( 0 ..total_num_transactions / packets_per_batch)
173
+ . map ( |_| pubkey:: new_rand ( ) )
174
+ . collect ( ) ;
175
+
176
+ ( 0 ..total_num_transactions)
177
+ . into_par_iter ( )
178
+ . map ( |i| {
179
+ let is_simulated_mint = is_simulated_mint_transaction (
180
+ simulate_mint,
181
+ i,
182
+ packets_per_batch,
183
+ mint_txs_percentage,
184
+ ) ;
185
+ // simulated mint transactions have higher compute-unit-price
186
+ let compute_unit_price = if is_simulated_mint { 5 } else { 1 } ;
187
+ let mut new = make_transfer_transaction_with_compute_unit_price (
188
+ & payer_keypair,
189
+ & to_pubkey,
190
+ 1 ,
191
+ hash,
192
+ compute_unit_price,
193
+ ) ;
194
+ let sig: [ u8 ; 64 ] = std:: array:: from_fn ( |_| thread_rng ( ) . gen :: < u8 > ( ) ) ;
195
+ new. message . account_keys [ 0 ] = pubkey:: new_rand ( ) ;
196
+ new. message . account_keys [ 1 ] = match contention {
197
+ WriteLockContention :: None => pubkey:: new_rand ( ) ,
198
+ WriteLockContention :: SameBatchOnly => {
199
+ // simulated mint transactions have conflict accounts
200
+ if is_simulated_mint {
201
+ chunk_pubkeys[ i / packets_per_batch]
202
+ } else {
203
+ pubkey:: new_rand ( )
204
+ }
148
205
}
149
- }
150
- WriteLockContention :: Full => to_pubkey,
151
- } ;
152
- new. signatures = vec ! [ Signature :: from( sig) ] ;
153
- new
154
- } )
155
- . collect ( )
206
+ WriteLockContention :: Full => to_pubkey,
207
+ WriteLockContention :: AccountContention ( _) => unreachable ! ( ) ,
208
+ } ;
209
+ new. signatures = vec ! [ Signature :: from( sig) ] ;
210
+ new
211
+ } )
212
+ . collect ( )
213
+ }
156
214
}
157
215
158
216
// In simulating mint, `mint_txs_percentage` transactions in a batch are mint transaction
@@ -271,8 +329,8 @@ fn main() {
271
329
Arg :: new ( "write_lock_contention" )
272
330
. long ( "write-lock-contention" )
273
331
. takes_value ( true )
274
- . possible_values ( WriteLockContention :: possible_values ( ) )
275
- . help ( "Accounts that test transactions write lock" ) ,
332
+ . value_parser ( parse_write_lock_contention )
333
+ . help ( "Accounts that test transactions write lock. Use 'none', 'same-batch-only', 'full',or 'n'/'account-contention n' where n is an integer greater than 1 " ) ,
276
334
)
277
335
. arg (
278
336
Arg :: new ( "batches_per_iteration" )
@@ -336,7 +394,8 @@ fn main() {
336
394
. value_of_t :: < usize > ( "batches_per_iteration" )
337
395
. unwrap_or ( BankingStage :: num_threads ( ) as usize ) ;
338
396
let write_lock_contention = matches
339
- . value_of_t :: < WriteLockContention > ( "write_lock_contention" )
397
+ . get_one :: < WriteLockContention > ( "write_lock_contention" )
398
+ . copied ( )
340
399
. unwrap_or ( WriteLockContention :: None ) ;
341
400
let mint_txs_percentage = matches
342
401
. value_of_t :: < usize > ( "mint_txs_percentage" )
@@ -397,6 +456,7 @@ fn main() {
397
456
let sig: [ u8 ; 64 ] = std:: array:: from_fn ( |_| thread_rng ( ) . gen :: < u8 > ( ) ) ;
398
457
fund. signatures = vec ! [ Signature :: from( sig) ] ;
399
458
bank. process_transaction ( & fund) . unwrap ( ) ;
459
+ bank. clear_signatures ( ) ; // needed for account contention because there is a non-zero chance of funding the same address.
400
460
} ) ;
401
461
} ) ;
402
462
0 commit comments