main.rs 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
  1. //! DoS tool
  2. //!
  3. //! Sends requests to cluster in a loop to measure
  4. //! the effect of handling these requests on the performance of the cluster.
  5. //!
  6. //! * `mode` argument defines interface to use (e.g. rpc, tvu, tpu)
  7. //! * `data-type` argument specifies the type of the request.
  8. //!
  9. //! Some request types might be used only with particular `mode` value.
  10. //! For example, `get-account-info` is valid only with `mode=rpc`.
  11. //!
  12. //! Most options are provided for `data-type = transaction`.
  13. //! These options allow to compose transaction which fails at
  14. //! a particular stage of the processing pipeline.
  15. //!
  16. //! To limit the number of possible options and simplify the usage of the tool,
  17. //! The following configurations are suggested:
  18. //! Let `COMMON="--mode tpu --data-type transaction --unique-transactions"`
  19. //! 1. Without blockhash or payer:
  20. //! 1.1 With invalid signatures
  21. //! ```bash
  22. //! solana-dos $COMMON --num-signatures 8
  23. //! ```
  24. //! 1.2 With valid signatures
  25. //! ```bash
  26. //! solana-dos $COMMON --valid-signatures --num-signatures 8
  27. //! ```
  28. //! 2. With blockhash and payer:
  29. //! 2.1 Single-instruction transaction
  30. //! ```bash
  31. //! solana-dos $COMMON --valid-blockhash --transaction-type transfer --num-instructions 1
  32. //! ```
  33. //! 2.2 Multi-instruction transaction
  34. //! ```bash
  35. //! solana-dos $COMMON --valid-blockhash --transaction-type transfer --num-instructions 8
  36. //! ```
  37. //! 2.3 Account-creation transaction
  38. //! ```bash
  39. //! solana-dos $COMMON --valid-blockhash --transaction-type account-creation
  40. //! ```
  41. //!
  42. #![allow(clippy::arithmetic_side_effects)]
  43. #![allow(deprecated)]
  44. use {
  45. crossbeam_channel::{select, tick, unbounded, Receiver, Sender},
  46. itertools::Itertools,
  47. log::*,
  48. rand::{thread_rng, Rng},
  49. solana_bench_tps::bench::generate_and_fund_keypairs,
  50. solana_client::{connection_cache::ConnectionCache, tpu_client::TpuClientWrapper},
  51. solana_connection_cache::client_connection::ClientConnection as TpuConnection,
  52. solana_core::repair::serve_repair::{RepairProtocol, RepairRequestHeader, ServeRepair},
  53. solana_dos::cli::*,
  54. solana_gossip::{
  55. contact_info::{ContactInfo, Protocol},
  56. gossip_service::{discover, get_client},
  57. },
  58. solana_hash::Hash,
  59. solana_keypair::Keypair,
  60. solana_measure::measure::Measure,
  61. solana_message::{compiled_instruction::CompiledInstruction, Message},
  62. solana_net_utils::bind_to_unspecified,
  63. solana_pubkey::Pubkey,
  64. solana_rpc_client::rpc_client::RpcClient,
  65. solana_signature::Signature,
  66. solana_signer::Signer,
  67. solana_stake_interface as stake,
  68. solana_streamer::socket::SocketAddrSpace,
  69. solana_system_interface::{
  70. instruction::{self as system_instruction, SystemInstruction},
  71. program as system_program,
  72. },
  73. solana_time_utils::timestamp,
  74. solana_tps_client::TpsClient,
  75. solana_tpu_client::tpu_client::DEFAULT_TPU_CONNECTION_POOL_SIZE,
  76. solana_transaction::Transaction,
  77. std::{
  78. net::SocketAddr,
  79. process::exit,
  80. sync::Arc,
  81. thread,
  82. time::{Duration, Instant},
  83. },
  84. };
  85. const PROGRESS_TIMEOUT_S: u64 = 120;
  86. const SAMPLE_PERIOD_MS: u64 = 10_000;
  87. fn compute_rate_per_second(count: usize) -> usize {
  88. (count * 1000) / (SAMPLE_PERIOD_MS as usize)
  89. }
  90. /// Provide functionality to generate several types of transactions:
  91. ///
  92. /// 1. Without blockhash
  93. /// 1.1 With valid signatures (number of signatures is configurable)
  94. /// 1.2 With invalid signatures (number of signatures is configurable)
  95. ///
  96. /// 2. With blockhash (but still deliberately invalid):
  97. /// 2.1 Transfer from 1 payer to multiple destinations (many instructions per transaction)
  98. /// 2.2 Create an account
  99. ///
  100. #[derive(Clone)]
  101. struct TransactionGenerator {
  102. blockhash: Hash,
  103. last_generated: Instant,
  104. transaction_params: TransactionParams,
  105. }
  106. impl TransactionGenerator {
  107. fn new(transaction_params: TransactionParams) -> Self {
  108. TransactionGenerator {
  109. blockhash: Hash::default(),
  110. last_generated: Instant::now()
  111. .checked_sub(Duration::from_secs(100))
  112. .unwrap(), //to force generation when generate is called
  113. transaction_params,
  114. }
  115. }
  116. /// Generate transaction
  117. ///
  118. /// `payer` - the account responsible for paying the cost of executing transaction, used as
  119. /// a source for transfer instructions and as funding account for create-account instructions.
  120. /// `destinations` - depending on the transaction type, might be destination accounts receiving transfers,
  121. /// new accounts, signing accounts. It is `None` only if `valid_signatures==false`.
  122. /// `client` - structure responsible for providing blockhash.
  123. ///
  124. fn generate<T: 'static + TpsClient + Send + Sync>(
  125. &mut self,
  126. payer: Option<&Keypair>,
  127. destinations: Option<Vec<&Keypair>>,
  128. client: Option<&Arc<T>>,
  129. ) -> Transaction {
  130. if self.transaction_params.valid_blockhash {
  131. let client = client.as_ref().unwrap();
  132. let destinations = destinations.unwrap();
  133. let payer = payer.as_ref().unwrap();
  134. self.generate_with_blockhash(payer, destinations, client)
  135. } else {
  136. self.generate_without_blockhash(destinations)
  137. }
  138. }
  139. fn generate_with_blockhash<T: 'static + TpsClient + Send + Sync>(
  140. &mut self,
  141. payer: &Keypair,
  142. destinations: Vec<&Keypair>,
  143. client: &Arc<T>,
  144. ) -> Transaction {
  145. // generate a new blockhash every 1sec
  146. if self.transaction_params.valid_blockhash
  147. && self.last_generated.elapsed().as_millis() > 1000
  148. {
  149. self.blockhash = client.get_latest_blockhash().unwrap();
  150. self.last_generated = Instant::now();
  151. }
  152. // transaction_type is known to be present because it is required by blockhash option in cli
  153. let transaction_type = self.transaction_params.transaction_type.as_ref().unwrap();
  154. match transaction_type {
  155. TransactionType::Transfer => {
  156. self.create_multi_transfer_transaction(payer, &destinations)
  157. }
  158. TransactionType::AccountCreation => {
  159. self.create_account_transaction(payer, destinations[0])
  160. }
  161. }
  162. }
  163. /// Create a transaction which transfers some lamports from payer to several destinations
  164. fn create_multi_transfer_transaction(&self, payer: &Keypair, to: &[&Keypair]) -> Transaction {
  165. let to_transfer: u64 = 500_000_000; // specify amount which will cause error
  166. let to: Vec<(Pubkey, u64)> = to.iter().map(|to| (to.pubkey(), to_transfer)).collect();
  167. let instructions = system_instruction::transfer_many(&payer.pubkey(), to.as_slice());
  168. let message = Message::new(&instructions, Some(&payer.pubkey()));
  169. let mut tx = Transaction::new_unsigned(message);
  170. tx.sign(&[payer], self.blockhash);
  171. tx
  172. }
  173. /// Create a transaction which opens account
  174. fn create_account_transaction(&self, payer: &Keypair, to: &Keypair) -> Transaction {
  175. let program_id = system_program::id(); // some valid program id
  176. let balance = 500_000_000;
  177. let space = 1024;
  178. let instructions = vec![system_instruction::create_account(
  179. &payer.pubkey(),
  180. &to.pubkey(),
  181. balance,
  182. space,
  183. &program_id,
  184. )];
  185. let message = Message::new(&instructions, Some(&payer.pubkey()));
  186. let signers: Vec<&Keypair> = vec![payer, to];
  187. Transaction::new(&signers, message, self.blockhash)
  188. }
  189. fn generate_without_blockhash(
  190. &mut self,
  191. destinations: Option<Vec<&Keypair>>, // provided for valid signatures
  192. ) -> Transaction {
  193. // create an arbitrary valid instruction
  194. let lamports = 5;
  195. let transfer_instruction = SystemInstruction::Transfer { lamports };
  196. let program_ids = vec![system_program::id(), stake::program::id()];
  197. let instructions = vec![CompiledInstruction::new(
  198. 0,
  199. &transfer_instruction,
  200. vec![0, 1],
  201. )];
  202. if self.transaction_params.valid_signatures {
  203. // Since we don't provide a payer, this transaction will end up
  204. // filtered at legacy.rs sanitize method (banking_stage) with error "a program cannot be payer"
  205. let destinations = destinations.unwrap();
  206. Transaction::new_with_compiled_instructions(
  207. &destinations,
  208. &[],
  209. self.blockhash,
  210. program_ids,
  211. instructions,
  212. )
  213. } else {
  214. // Since we provided invalid signatures
  215. // this transaction will end up filtered at legacy.rs (banking_stage) because
  216. // num_required_signatures == 0
  217. let mut tx = Transaction::new_with_compiled_instructions(
  218. &[] as &[&Keypair; 0],
  219. &[],
  220. self.blockhash,
  221. program_ids,
  222. instructions,
  223. );
  224. let num_signatures = self.transaction_params.num_signatures.unwrap();
  225. tx.signatures = vec![Signature::new_unique(); num_signatures];
  226. tx
  227. }
  228. }
  229. }
  230. // Multithreading-related functions
  231. //
  232. // The most computationally expensive work is signing new transactions.
  233. // Here we generate them in `num_gen_threads` threads.
  234. //
  235. struct TransactionBatchMsg {
  236. batch: Vec<Vec<u8>>,
  237. gen_time: u64,
  238. }
  239. /// Creates thread which receives batches of transactions from tx_receiver
  240. /// and sends them to the target.
  241. /// If `iterations` is 0, it works indefenetely.
  242. /// Otherwise, it sends at least `iterations` number of transactions
  243. fn create_sender_thread(
  244. tx_receiver: Receiver<TransactionBatchMsg>,
  245. iterations: usize,
  246. target: &SocketAddr,
  247. tpu_use_quic: bool,
  248. ) -> thread::JoinHandle<()> {
  249. // ConnectionCache is used instead of client because it gives ~6% higher pps
  250. let connection_cache = if tpu_use_quic {
  251. ConnectionCache::new_quic(
  252. "connection_cache_dos_quic",
  253. DEFAULT_TPU_CONNECTION_POOL_SIZE,
  254. )
  255. } else {
  256. ConnectionCache::with_udp("connection_cache_dos_udp", DEFAULT_TPU_CONNECTION_POOL_SIZE)
  257. };
  258. let connection = connection_cache.get_connection(target);
  259. let stats_timer_receiver = tick(Duration::from_millis(SAMPLE_PERIOD_MS));
  260. let progress_timer_receiver = tick(Duration::from_secs(PROGRESS_TIMEOUT_S));
  261. let mut time_send_ns = 0;
  262. let mut time_generate_ns = 0;
  263. // Sender signals to stop Generators by dropping receiver.
  264. // It happens in 2 cases:
  265. // * Sender has sent at least `iterations` number of transactions
  266. // * Sender observes that there is no progress. Since there is no way to use recv_timeout with select,
  267. // a timer is used.
  268. thread::Builder::new().name("Sender".to_string()).spawn(move || {
  269. let mut total_count: usize = 0;
  270. let mut prev_total_count = 0; // to track progress
  271. let mut stats_count: usize = 0;
  272. let mut stats_error_count: usize = 0;
  273. loop {
  274. select! {
  275. recv(tx_receiver) -> msg => {
  276. match msg {
  277. Ok(tx_batch) => {
  278. let len = tx_batch.batch.len();
  279. let mut measure_send_txs = Measure::start("measure_send_txs");
  280. let res = connection.send_data_batch_async(tx_batch.batch);
  281. measure_send_txs.stop();
  282. time_send_ns += measure_send_txs.as_ns();
  283. time_generate_ns += tx_batch.gen_time;
  284. if res.is_err() {
  285. stats_error_count += len;
  286. }
  287. stats_count += len;
  288. total_count += len;
  289. if iterations != 0 && total_count >= iterations {
  290. info!("All transactions have been sent");
  291. // dropping receiver to signal generator threads to stop
  292. drop(tx_receiver);
  293. break;
  294. }
  295. }
  296. _ => panic!("Sender panics"),
  297. }
  298. },
  299. recv(stats_timer_receiver) -> _ => {
  300. info!("tx_receiver queue len: {}", tx_receiver.len());
  301. info!("Count: {}, error count: {}, send mean time: {}, generate mean time: {}, rps: {}",
  302. stats_count,
  303. stats_error_count,
  304. time_send_ns.checked_div(stats_count as u64).unwrap_or(0),
  305. time_generate_ns.checked_div(stats_count as u64).unwrap_or(0),
  306. compute_rate_per_second(stats_count),
  307. );
  308. stats_count = 0;
  309. stats_error_count = 0;
  310. time_send_ns = 0;
  311. time_generate_ns = 0;
  312. },
  313. recv(progress_timer_receiver) -> _ => {
  314. if prev_total_count - total_count == 0 {
  315. info!("No progress, stop execution");
  316. // dropping receiver to signal generator threads to stop
  317. drop(tx_receiver);
  318. break;
  319. }
  320. prev_total_count = total_count;
  321. }
  322. }
  323. }
  324. }).unwrap()
  325. }
  326. fn create_generator_thread<T: 'static + TpsClient + Send + Sync>(
  327. tx_sender: &Sender<TransactionBatchMsg>,
  328. send_batch_size: usize,
  329. transaction_generator: &TransactionGenerator,
  330. client: Option<Arc<T>>,
  331. payer: Option<Keypair>,
  332. ) -> thread::JoinHandle<()> {
  333. let tx_sender = tx_sender.clone();
  334. let mut transaction_generator = transaction_generator.clone();
  335. let transaction_params: &TransactionParams = &transaction_generator.transaction_params;
  336. // Generate n=1000 unique keypairs
  337. // The number of chunks is described by binomial coefficient
  338. // and hence this choice of n provides large enough number of permutations
  339. let mut keypairs_flat: Vec<Keypair> = Vec::new();
  340. // 1000 is arbitrary number. In case of permutation_size > 1,
  341. // this guarantees large enough set of unique permutations
  342. let permutation_size = get_permutation_size(
  343. transaction_params.num_signatures.as_ref(),
  344. transaction_params.num_instructions.as_ref(),
  345. );
  346. let num_keypairs = 1000 * permutation_size;
  347. let generate_keypairs =
  348. transaction_params.valid_signatures || transaction_params.valid_blockhash;
  349. if generate_keypairs {
  350. keypairs_flat = (0..num_keypairs).map(|_| Keypair::new()).collect();
  351. }
  352. thread::Builder::new()
  353. .name("Generator".to_string())
  354. .spawn(move || {
  355. let indexes: Vec<usize> = (0..keypairs_flat.len()).collect();
  356. let mut it = indexes.iter().permutations(permutation_size);
  357. loop {
  358. let mut data = Vec::<Vec<u8>>::with_capacity(send_batch_size);
  359. let mut measure_generate_txs = Measure::start("measure_generate_txs");
  360. for _ in 0..send_batch_size {
  361. let chunk_keypairs = if generate_keypairs {
  362. let mut permutation = it.next();
  363. if permutation.is_none() {
  364. // if ran out of permutations, regenerate keys
  365. keypairs_flat.iter_mut().for_each(|v| *v = Keypair::new());
  366. info!("Regenerate keypairs");
  367. permutation = it.next();
  368. }
  369. let permutation = permutation.unwrap();
  370. Some(apply_permutation(permutation, &keypairs_flat))
  371. } else {
  372. None
  373. };
  374. let tx = transaction_generator.generate(
  375. payer.as_ref(),
  376. chunk_keypairs,
  377. client.as_ref(),
  378. );
  379. data.push(bincode::serialize(&tx).unwrap());
  380. }
  381. measure_generate_txs.stop();
  382. let result = tx_sender.send(TransactionBatchMsg {
  383. batch: data,
  384. gen_time: measure_generate_txs.as_ns(),
  385. });
  386. if result.is_err() {
  387. // means that receiver has been dropped by sender thread
  388. info!("Exit generator thread");
  389. break;
  390. }
  391. }
  392. })
  393. .unwrap()
  394. }
  395. fn get_target(
  396. nodes: &[ContactInfo],
  397. mode: Mode,
  398. entrypoint_addr: SocketAddr,
  399. tpu_use_quic: bool,
  400. ) -> Option<(Pubkey, SocketAddr)> {
  401. let protocol = if tpu_use_quic {
  402. Protocol::QUIC
  403. } else {
  404. Protocol::UDP
  405. };
  406. let mut target = None;
  407. if nodes.is_empty() {
  408. // skip-gossip case
  409. target = Some((solana_pubkey::new_rand(), entrypoint_addr));
  410. } else {
  411. info!("************ NODE ***********");
  412. for node in nodes {
  413. info!("{node:?}");
  414. }
  415. info!("ADDR = {entrypoint_addr}");
  416. for node in nodes {
  417. if node.gossip() == Some(entrypoint_addr) {
  418. info!("{:?}", node.gossip());
  419. target = match mode {
  420. Mode::Gossip => Some((*node.pubkey(), node.gossip().unwrap())),
  421. Mode::Tvu => Some((*node.pubkey(), node.tvu(Protocol::UDP).unwrap())),
  422. Mode::Tpu => Some((*node.pubkey(), node.tpu(protocol).unwrap())),
  423. Mode::TpuForwards => {
  424. Some((*node.pubkey(), node.tpu_forwards(protocol).unwrap()))
  425. }
  426. Mode::Repair => todo!("repair socket is not gossiped anymore!"),
  427. Mode::ServeRepair => {
  428. Some((*node.pubkey(), node.serve_repair(Protocol::UDP).unwrap()))
  429. }
  430. Mode::Rpc => None,
  431. };
  432. break;
  433. }
  434. }
  435. }
  436. target
  437. }
  438. fn get_rpc_client(
  439. nodes: &[ContactInfo],
  440. entrypoint_addr: SocketAddr,
  441. ) -> Result<RpcClient, &'static str> {
  442. if nodes.is_empty() {
  443. // skip-gossip case
  444. return Ok(RpcClient::new_socket(entrypoint_addr));
  445. }
  446. // find target node
  447. for node in nodes {
  448. if node.gossip() == Some(entrypoint_addr) {
  449. info!("{:?}", node.gossip());
  450. return Ok(RpcClient::new_socket(node.rpc().unwrap()));
  451. }
  452. }
  453. Err("Node with entrypoint_addr was not found")
  454. }
  455. fn run_dos_rpc_mode_helper<F: Fn() -> bool>(iterations: usize, rpc_client_call: F) {
  456. let mut last_log = Instant::now();
  457. let mut total_count: usize = 0;
  458. let mut count = 0;
  459. let mut error_count = 0;
  460. loop {
  461. if !rpc_client_call() {
  462. error_count += 1;
  463. }
  464. count += 1;
  465. total_count += 1;
  466. if last_log.elapsed().as_millis() > SAMPLE_PERIOD_MS as u128 {
  467. info!(
  468. "count: {}, errors: {}, rps: {}",
  469. count,
  470. error_count,
  471. compute_rate_per_second(count)
  472. );
  473. last_log = Instant::now();
  474. count = 0;
  475. }
  476. if iterations != 0 && total_count >= iterations {
  477. break;
  478. }
  479. }
  480. }
  481. fn run_dos_rpc_mode(
  482. rpc_client: RpcClient,
  483. iterations: usize,
  484. data_type: DataType,
  485. data_input: &Pubkey,
  486. ) {
  487. match data_type {
  488. DataType::GetAccountInfo => {
  489. run_dos_rpc_mode_helper(iterations, || -> bool {
  490. rpc_client.get_account(data_input).is_ok()
  491. });
  492. }
  493. DataType::GetProgramAccounts => {
  494. run_dos_rpc_mode_helper(iterations, || -> bool {
  495. rpc_client.get_program_accounts(data_input).is_ok()
  496. });
  497. }
  498. _ => {
  499. panic!("unsupported data type");
  500. }
  501. }
  502. }
  503. /// Apply given permutation to the vector of items
  504. fn apply_permutation<'a, T>(permutation: Vec<&usize>, items: &'a [T]) -> Vec<&'a T> {
  505. let mut res = Vec::with_capacity(permutation.len());
  506. for i in permutation {
  507. res.push(&items[*i]);
  508. }
  509. res
  510. }
  511. fn create_payers<T: 'static + TpsClient + Send + Sync>(
  512. valid_blockhash: bool,
  513. size: usize,
  514. client: Option<&Arc<T>>,
  515. ) -> Vec<Option<Keypair>> {
  516. // Assume that if we use valid blockhash, we also have a payer
  517. if valid_blockhash {
  518. // each payer is used to fund transaction
  519. // transactions are built to be invalid so the amount here is arbitrary
  520. let funding_key = Keypair::new();
  521. let funding_key = Arc::new(funding_key);
  522. let res = generate_and_fund_keypairs(
  523. client.unwrap().clone(),
  524. &funding_key,
  525. size,
  526. 1_000_000,
  527. false,
  528. false,
  529. )
  530. .unwrap_or_else(|e| {
  531. eprintln!("Error could not fund keys: {e:?}");
  532. exit(1);
  533. });
  534. res.into_iter().map(Some).collect()
  535. } else {
  536. std::iter::repeat_with(|| None).take(size).collect()
  537. }
  538. }
  539. fn get_permutation_size(num_signatures: Option<&usize>, num_instructions: Option<&usize>) -> usize {
  540. if let Some(num_signatures) = num_signatures {
  541. *num_signatures
  542. } else if let Some(num_instructions) = num_instructions {
  543. *num_instructions
  544. } else {
  545. 1 // for the case AccountCreation
  546. }
  547. }
  548. fn run_dos_transactions<T: 'static + TpsClient + Send + Sync>(
  549. target: SocketAddr,
  550. iterations: usize,
  551. client: Option<Arc<T>>,
  552. transaction_params: TransactionParams,
  553. tpu_use_quic: bool,
  554. num_gen_threads: usize,
  555. send_batch_size: usize,
  556. ) {
  557. // Number of payers is the number of generating threads
  558. // Later, we will create a new payer for each thread since Keypair is not clonable
  559. let payers: Vec<Option<Keypair>> = create_payers(
  560. transaction_params.valid_blockhash,
  561. num_gen_threads,
  562. client.as_ref(),
  563. );
  564. let transaction_generator = TransactionGenerator::new(transaction_params);
  565. let (tx_sender, tx_receiver) = unbounded();
  566. let sender_thread = create_sender_thread(tx_receiver, iterations, &target, tpu_use_quic);
  567. let tx_generator_threads: Vec<_> = payers
  568. .into_iter()
  569. .map(|payer| {
  570. create_generator_thread(
  571. &tx_sender,
  572. send_batch_size,
  573. &transaction_generator,
  574. client.clone(),
  575. payer,
  576. )
  577. })
  578. .collect();
  579. if let Err(err) = sender_thread.join() {
  580. println!("join() failed with: {err:?}");
  581. }
  582. for t_generator in tx_generator_threads {
  583. if let Err(err) = t_generator.join() {
  584. println!("join() failed with: {err:?}");
  585. }
  586. }
  587. }
  588. fn run_dos<T: 'static + TpsClient + Send + Sync>(
  589. nodes: &[ContactInfo],
  590. iterations: usize,
  591. client: Option<Arc<T>>,
  592. params: DosClientParameters,
  593. ) {
  594. let target = get_target(
  595. nodes,
  596. params.mode,
  597. params.entrypoint_addr,
  598. params.tpu_use_quic,
  599. );
  600. if params.mode == Mode::Rpc {
  601. // creating rpc_client because get_account, get_program_accounts are not implemented for TpsClient
  602. let rpc_client =
  603. get_rpc_client(nodes, params.entrypoint_addr).expect("Failed to get rpc client");
  604. // existence of data_input is checked at cli level
  605. run_dos_rpc_mode(
  606. rpc_client,
  607. iterations,
  608. params.data_type,
  609. &params.data_input.unwrap(),
  610. );
  611. } else if params.data_type == DataType::Transaction
  612. && params.transaction_params.unique_transactions
  613. {
  614. let (_, target_addr) = target.expect("should have target");
  615. info!("Targeting {target_addr}");
  616. run_dos_transactions(
  617. target_addr,
  618. iterations,
  619. client,
  620. params.transaction_params,
  621. params.tpu_use_quic,
  622. params.num_gen_threads,
  623. params.send_batch_size,
  624. );
  625. } else {
  626. let (target_id, target_addr) = target.expect("should have target");
  627. info!("Targeting {target_addr}");
  628. let mut data = match params.data_type {
  629. DataType::RepairHighest => {
  630. let slot = 100;
  631. let keypair = Keypair::new();
  632. let header = RepairRequestHeader::new(keypair.pubkey(), target_id, timestamp(), 0);
  633. let req = RepairProtocol::WindowIndex {
  634. header,
  635. slot,
  636. shred_index: 0,
  637. };
  638. ServeRepair::repair_proto_to_bytes(&req, &keypair).unwrap()
  639. }
  640. DataType::RepairShred => {
  641. let slot = 100;
  642. let keypair = Keypair::new();
  643. let header = RepairRequestHeader::new(keypair.pubkey(), target_id, timestamp(), 0);
  644. let req = RepairProtocol::HighestWindowIndex {
  645. header,
  646. slot,
  647. shred_index: 0,
  648. };
  649. ServeRepair::repair_proto_to_bytes(&req, &keypair).unwrap()
  650. }
  651. DataType::RepairOrphan => {
  652. let slot = 100;
  653. let keypair = Keypair::new();
  654. let header = RepairRequestHeader::new(keypair.pubkey(), target_id, timestamp(), 0);
  655. let req = RepairProtocol::Orphan { header, slot };
  656. ServeRepair::repair_proto_to_bytes(&req, &keypair).unwrap()
  657. }
  658. DataType::Random => {
  659. vec![0; params.data_size]
  660. }
  661. DataType::Transaction => {
  662. let tp = params.transaction_params;
  663. info!("{tp:?}");
  664. let valid_blockhash = tp.valid_blockhash;
  665. let payers: Vec<Option<Keypair>> =
  666. create_payers(valid_blockhash, 1, client.as_ref());
  667. let payer = payers[0].as_ref();
  668. let permutation_size =
  669. get_permutation_size(tp.num_signatures.as_ref(), tp.num_instructions.as_ref());
  670. let keypairs: Vec<Keypair> =
  671. (0..permutation_size).map(|_| Keypair::new()).collect();
  672. let keypairs_chunk: Option<Vec<&Keypair>> =
  673. if tp.valid_signatures || tp.valid_blockhash {
  674. Some(keypairs.iter().collect())
  675. } else {
  676. None
  677. };
  678. let mut transaction_generator = TransactionGenerator::new(tp);
  679. let tx = transaction_generator.generate(payer, keypairs_chunk, client.as_ref());
  680. info!("{tx:?}");
  681. bincode::serialize(&tx).unwrap()
  682. }
  683. _ => panic!("Unsupported data_type detected"),
  684. };
  685. let socket = bind_to_unspecified().unwrap();
  686. let mut last_log = Instant::now();
  687. let mut total_count: usize = 0;
  688. let mut count: usize = 0;
  689. let mut error_count = 0;
  690. loop {
  691. if params.data_type == DataType::Random {
  692. thread_rng().fill(&mut data[..]);
  693. }
  694. let res = socket.send_to(&data, target_addr);
  695. if res.is_err() {
  696. error_count += 1;
  697. }
  698. count += 1;
  699. total_count += 1;
  700. if last_log.elapsed().as_millis() > SAMPLE_PERIOD_MS as u128 {
  701. info!(
  702. "count: {}, errors: {}, rps: {}",
  703. count,
  704. error_count,
  705. compute_rate_per_second(count)
  706. );
  707. last_log = Instant::now();
  708. count = 0;
  709. }
  710. if iterations != 0 && total_count >= iterations {
  711. break;
  712. }
  713. }
  714. }
  715. }
  716. fn main() {
  717. agave_logger::setup_with_default_filter();
  718. let mut cmd_params = build_cli_parameters();
  719. if !cmd_params.skip_gossip && cmd_params.shred_version.is_none() {
  720. // Try to get shred version from the entrypoint
  721. cmd_params.shred_version = Some(
  722. solana_net_utils::get_cluster_shred_version(&cmd_params.entrypoint_addr)
  723. .unwrap_or_else(|err| {
  724. eprintln!("Failed to get shred version: {err}");
  725. exit(1);
  726. }),
  727. );
  728. }
  729. let (nodes, client) = if !cmd_params.skip_gossip {
  730. info!("Finding cluster entry: {:?}", cmd_params.entrypoint_addr);
  731. let socket_addr_space = SocketAddrSpace::new(cmd_params.allow_private_addr);
  732. let (gossip_nodes, validators) = discover(
  733. None, // keypair
  734. Some(&cmd_params.entrypoint_addr),
  735. None, // num_nodes
  736. Duration::from_secs(60), // timeout
  737. None, // find_nodes_by_pubkey
  738. Some(&cmd_params.entrypoint_addr), // find_node_by_gossip_addr
  739. None, // my_gossip_addr
  740. cmd_params.shred_version.unwrap(), // my_shred_version
  741. socket_addr_space,
  742. )
  743. .unwrap_or_else(|err| {
  744. eprintln!(
  745. "Failed to discover {} node: {:?}",
  746. cmd_params.entrypoint_addr, err
  747. );
  748. exit(1);
  749. });
  750. let connection_cache = match cmd_params.tpu_use_quic {
  751. true => ConnectionCache::new_quic(
  752. "connection_cache_dos_quic",
  753. DEFAULT_TPU_CONNECTION_POOL_SIZE,
  754. ),
  755. false => ConnectionCache::with_udp(
  756. "connection_cache_dos_udp",
  757. DEFAULT_TPU_CONNECTION_POOL_SIZE,
  758. ),
  759. };
  760. let client = get_client(&validators, Arc::new(connection_cache));
  761. (gossip_nodes, Some(client))
  762. } else {
  763. (vec![], None)
  764. };
  765. info!("done found {} nodes", nodes.len());
  766. if let Some(tpu_client) = client {
  767. match tpu_client {
  768. TpuClientWrapper::Quic(quic_client) => {
  769. run_dos(&nodes, 0, Some(Arc::new(quic_client)), cmd_params);
  770. }
  771. TpuClientWrapper::Udp(udp_client) => {
  772. run_dos(&nodes, 0, Some(Arc::new(udp_client)), cmd_params);
  773. }
  774. };
  775. }
  776. }
  777. #[cfg(test)]
  778. pub mod test {
  779. use {
  780. super::*,
  781. solana_core::validator::ValidatorConfig,
  782. solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
  783. solana_local_cluster::{
  784. cluster::Cluster,
  785. local_cluster::{ClusterConfig, LocalCluster},
  786. validator_configs::make_identical_validator_configs,
  787. },
  788. solana_quic_client::{QuicConfig, QuicConnectionManager, QuicPool},
  789. solana_rpc::rpc::JsonRpcConfig,
  790. solana_time_utils::timestamp,
  791. solana_tpu_client::tpu_client::TpuClient,
  792. };
  793. const TEST_SEND_BATCH_SIZE: usize = 1;
  794. // thin wrapper for the run_dos function
  795. // to avoid specifying everywhere generic parameters
  796. fn run_dos_no_client(nodes: &[ContactInfo], iterations: usize, params: DosClientParameters) {
  797. run_dos::<TpuClient<QuicPool, QuicConnectionManager, QuicConfig>>(
  798. nodes, iterations, None, params,
  799. );
  800. }
  801. #[test]
  802. fn test_dos() {
  803. let nodes = [ContactInfo::new_localhost(
  804. &solana_pubkey::new_rand(),
  805. timestamp(),
  806. )];
  807. let entrypoint_addr = nodes[0].gossip().unwrap();
  808. run_dos_no_client(
  809. &nodes,
  810. 1,
  811. DosClientParameters {
  812. entrypoint_addr,
  813. mode: Mode::Tvu,
  814. data_size: 10,
  815. data_type: DataType::Random,
  816. data_input: None,
  817. skip_gossip: false,
  818. shred_version: Some(42),
  819. allow_private_addr: false,
  820. num_gen_threads: 1,
  821. transaction_params: TransactionParams::default(),
  822. tpu_use_quic: false,
  823. send_batch_size: TEST_SEND_BATCH_SIZE,
  824. },
  825. );
  826. // TODO: Figure out how to DOS repair. Repair socket is no longer
  827. // gossiped and cannot be obtained from a node's contact-info.
  828. #[cfg(not(test))]
  829. run_dos_no_client(
  830. &nodes,
  831. 1,
  832. DosClientParameters {
  833. entrypoint_addr,
  834. mode: Mode::Repair,
  835. data_size: 10,
  836. data_type: DataType::RepairHighest,
  837. data_input: None,
  838. skip_gossip: false,
  839. shred_version: Some(42),
  840. allow_private_addr: false,
  841. num_gen_threads: 1,
  842. transaction_params: TransactionParams::default(),
  843. tpu_use_quic: false,
  844. send_batch_size: TEST_SEND_BATCH_SIZE,
  845. },
  846. );
  847. run_dos_no_client(
  848. &nodes,
  849. 1,
  850. DosClientParameters {
  851. entrypoint_addr,
  852. mode: Mode::ServeRepair,
  853. data_size: 10,
  854. data_type: DataType::RepairShred,
  855. data_input: None,
  856. skip_gossip: false,
  857. shred_version: Some(42),
  858. allow_private_addr: false,
  859. num_gen_threads: 1,
  860. transaction_params: TransactionParams::default(),
  861. tpu_use_quic: false,
  862. send_batch_size: TEST_SEND_BATCH_SIZE,
  863. },
  864. );
  865. run_dos_no_client(
  866. &nodes,
  867. 1,
  868. DosClientParameters {
  869. entrypoint_addr,
  870. mode: Mode::Rpc,
  871. data_size: 0,
  872. data_type: DataType::GetAccountInfo,
  873. data_input: Some(Pubkey::default()),
  874. skip_gossip: false,
  875. shred_version: Some(42),
  876. allow_private_addr: false,
  877. num_gen_threads: 1,
  878. transaction_params: TransactionParams::default(),
  879. tpu_use_quic: false,
  880. send_batch_size: TEST_SEND_BATCH_SIZE,
  881. },
  882. );
  883. }
  884. #[test]
  885. fn test_dos_random() {
  886. agave_logger::setup();
  887. let num_nodes = 1;
  888. let cluster =
  889. LocalCluster::new_with_equal_stakes(num_nodes, 100, 3, SocketAddrSpace::Unspecified);
  890. assert_eq!(cluster.validators.len(), num_nodes);
  891. let nodes = cluster.get_node_pubkeys();
  892. let node = cluster.get_contact_info(&nodes[0]).unwrap().clone();
  893. let nodes_slice = [node];
  894. // send random transactions to TPU
  895. // will be discarded on sigverify stage
  896. run_dos_no_client(
  897. &nodes_slice,
  898. 10,
  899. DosClientParameters {
  900. entrypoint_addr: cluster.entry_point_info.gossip().unwrap(),
  901. mode: Mode::Tpu,
  902. data_size: 1024,
  903. data_type: DataType::Random,
  904. data_input: None,
  905. skip_gossip: false,
  906. shred_version: Some(42),
  907. allow_private_addr: false,
  908. num_gen_threads: 1,
  909. transaction_params: TransactionParams::default(),
  910. tpu_use_quic: false,
  911. send_batch_size: TEST_SEND_BATCH_SIZE,
  912. },
  913. );
  914. }
  915. #[test]
  916. fn test_dos_without_blockhash() {
  917. agave_logger::setup();
  918. let num_nodes = 1;
  919. let cluster =
  920. LocalCluster::new_with_equal_stakes(num_nodes, 100, 3, SocketAddrSpace::Unspecified);
  921. assert_eq!(cluster.validators.len(), num_nodes);
  922. let nodes = cluster.get_node_pubkeys();
  923. let node = cluster.get_contact_info(&nodes[0]).unwrap().clone();
  924. let nodes_slice = [node];
  925. let client = Arc::new(
  926. cluster
  927. .build_validator_tpu_quic_client(cluster.entry_point_info.pubkey())
  928. .unwrap_or_else(|err| {
  929. panic!("Could not create TpuClient with Quic Cache {err:?}");
  930. }),
  931. );
  932. // creates one transaction with 8 valid signatures and sends it 10 times
  933. run_dos(
  934. &nodes_slice,
  935. 10,
  936. Some(client.clone()),
  937. DosClientParameters {
  938. entrypoint_addr: cluster.entry_point_info.gossip().unwrap(),
  939. mode: Mode::Tpu,
  940. data_size: 0, // irrelevant
  941. data_type: DataType::Transaction,
  942. data_input: None,
  943. skip_gossip: false,
  944. shred_version: Some(42),
  945. allow_private_addr: false,
  946. num_gen_threads: 1,
  947. transaction_params: TransactionParams {
  948. num_signatures: Some(8),
  949. valid_blockhash: false,
  950. valid_signatures: true,
  951. unique_transactions: false,
  952. transaction_type: None,
  953. num_instructions: None,
  954. },
  955. tpu_use_quic: false,
  956. send_batch_size: TEST_SEND_BATCH_SIZE,
  957. },
  958. );
  959. // creates and sends unique transactions which have invalid signatures
  960. run_dos(
  961. &nodes_slice,
  962. 10,
  963. Some(client.clone()),
  964. DosClientParameters {
  965. entrypoint_addr: cluster.entry_point_info.gossip().unwrap(),
  966. mode: Mode::Tpu,
  967. data_size: 0, // irrelevant
  968. data_type: DataType::Transaction,
  969. data_input: None,
  970. skip_gossip: false,
  971. shred_version: Some(42),
  972. allow_private_addr: false,
  973. num_gen_threads: 1,
  974. transaction_params: TransactionParams {
  975. num_signatures: Some(8),
  976. valid_blockhash: false,
  977. valid_signatures: false,
  978. unique_transactions: true,
  979. transaction_type: None,
  980. num_instructions: None,
  981. },
  982. tpu_use_quic: false,
  983. send_batch_size: TEST_SEND_BATCH_SIZE,
  984. },
  985. );
  986. // creates and sends unique transactions which have valid signatures
  987. run_dos(
  988. &nodes_slice,
  989. 10,
  990. Some(client),
  991. DosClientParameters {
  992. entrypoint_addr: cluster.entry_point_info.gossip().unwrap(),
  993. mode: Mode::Tpu,
  994. data_size: 0, // irrelevant
  995. data_type: DataType::Transaction,
  996. data_input: None,
  997. skip_gossip: false,
  998. shred_version: Some(42),
  999. allow_private_addr: false,
  1000. num_gen_threads: 1,
  1001. transaction_params: TransactionParams {
  1002. num_signatures: Some(8),
  1003. valid_blockhash: false,
  1004. valid_signatures: true,
  1005. unique_transactions: true,
  1006. transaction_type: None,
  1007. num_instructions: None,
  1008. },
  1009. tpu_use_quic: false,
  1010. send_batch_size: TEST_SEND_BATCH_SIZE,
  1011. },
  1012. );
  1013. }
  1014. fn run_dos_with_blockhash_and_payer(tpu_use_quic: bool) {
  1015. agave_logger::setup();
  1016. // 1. Create faucet thread
  1017. let faucet_keypair = Keypair::new();
  1018. let faucet_pubkey = faucet_keypair.pubkey();
  1019. let faucet_addr = run_local_faucet_with_unique_port_for_tests(faucet_keypair);
  1020. let mut validator_config = ValidatorConfig::default_for_test();
  1021. validator_config.rpc_config = JsonRpcConfig {
  1022. faucet_addr: Some(faucet_addr),
  1023. ..JsonRpcConfig::default_for_test()
  1024. };
  1025. // 2. Create a local cluster which is aware of faucet
  1026. let num_nodes = 1;
  1027. let cluster = LocalCluster::new(
  1028. &mut ClusterConfig {
  1029. node_stakes: vec![999_990; num_nodes],
  1030. mint_lamports: 200_000_000,
  1031. validator_configs: make_identical_validator_configs(
  1032. &ValidatorConfig {
  1033. rpc_config: JsonRpcConfig {
  1034. faucet_addr: Some(faucet_addr),
  1035. ..JsonRpcConfig::default_for_test()
  1036. },
  1037. ..ValidatorConfig::default_for_test()
  1038. },
  1039. num_nodes,
  1040. ),
  1041. ..ClusterConfig::default()
  1042. },
  1043. SocketAddrSpace::Unspecified,
  1044. );
  1045. assert_eq!(cluster.validators.len(), num_nodes);
  1046. // 3. Transfer funds to faucet account
  1047. cluster.transfer(&cluster.funding_keypair, &faucet_pubkey, 100_000_000);
  1048. let nodes = cluster.get_node_pubkeys();
  1049. let node = cluster.get_contact_info(&nodes[0]).unwrap().clone();
  1050. let nodes_slice = [node];
  1051. let client = Arc::new(
  1052. cluster
  1053. .build_validator_tpu_quic_client(cluster.entry_point_info.pubkey())
  1054. .unwrap_or_else(|err| {
  1055. panic!("Could not create TpuClient with Quic Cache {err:?}");
  1056. }),
  1057. );
  1058. // creates one transaction and sends it 10 times
  1059. // this is done in single thread
  1060. run_dos(
  1061. &nodes_slice,
  1062. 10,
  1063. Some(client.clone()),
  1064. DosClientParameters {
  1065. entrypoint_addr: cluster.entry_point_info.gossip().unwrap(),
  1066. mode: Mode::Tpu,
  1067. data_size: 0, // irrelevant if not random
  1068. data_type: DataType::Transaction,
  1069. data_input: None,
  1070. skip_gossip: false,
  1071. shred_version: Some(42),
  1072. allow_private_addr: false,
  1073. num_gen_threads: 1,
  1074. transaction_params: TransactionParams {
  1075. num_signatures: None,
  1076. valid_blockhash: true,
  1077. valid_signatures: true,
  1078. unique_transactions: false,
  1079. transaction_type: Some(TransactionType::Transfer),
  1080. num_instructions: Some(1),
  1081. },
  1082. tpu_use_quic,
  1083. send_batch_size: TEST_SEND_BATCH_SIZE,
  1084. },
  1085. );
  1086. // creates and sends unique transactions of Transfer
  1087. // which tries to send too much lamports from payer to one recipient
  1088. // it uses several threads
  1089. run_dos(
  1090. &nodes_slice,
  1091. 10,
  1092. Some(client.clone()),
  1093. DosClientParameters {
  1094. entrypoint_addr: cluster.entry_point_info.gossip().unwrap(),
  1095. mode: Mode::Tpu,
  1096. data_size: 0, // irrelevant if not random
  1097. data_type: DataType::Transaction,
  1098. data_input: None,
  1099. skip_gossip: false,
  1100. shred_version: Some(42),
  1101. allow_private_addr: false,
  1102. num_gen_threads: 1,
  1103. transaction_params: TransactionParams {
  1104. num_signatures: None,
  1105. valid_blockhash: true,
  1106. valid_signatures: true,
  1107. unique_transactions: true,
  1108. transaction_type: Some(TransactionType::Transfer),
  1109. num_instructions: Some(1),
  1110. },
  1111. tpu_use_quic,
  1112. send_batch_size: TEST_SEND_BATCH_SIZE,
  1113. },
  1114. );
  1115. // creates and sends unique transactions of type Transfer
  1116. // which tries to send too much lamports from payer to several recipients
  1117. // it uses several threads
  1118. run_dos(
  1119. &nodes_slice,
  1120. 10,
  1121. Some(client.clone()),
  1122. DosClientParameters {
  1123. entrypoint_addr: cluster.entry_point_info.gossip().unwrap(),
  1124. mode: Mode::Tpu,
  1125. data_size: 0, // irrelevant if not random
  1126. data_type: DataType::Transaction,
  1127. data_input: None,
  1128. skip_gossip: false,
  1129. shred_version: Some(42),
  1130. allow_private_addr: false,
  1131. num_gen_threads: 1,
  1132. transaction_params: TransactionParams {
  1133. num_signatures: None,
  1134. valid_blockhash: true,
  1135. valid_signatures: true,
  1136. unique_transactions: true,
  1137. transaction_type: Some(TransactionType::Transfer),
  1138. num_instructions: Some(8),
  1139. },
  1140. tpu_use_quic,
  1141. send_batch_size: TEST_SEND_BATCH_SIZE,
  1142. },
  1143. );
  1144. // creates and sends unique transactions of type CreateAccount
  1145. // which tries to create account with too large balance
  1146. // it uses several threads
  1147. run_dos(
  1148. &nodes_slice,
  1149. 10,
  1150. Some(client),
  1151. DosClientParameters {
  1152. entrypoint_addr: cluster.entry_point_info.gossip().unwrap(),
  1153. mode: Mode::Tpu,
  1154. data_size: 0, // irrelevant if not random
  1155. data_type: DataType::Transaction,
  1156. data_input: None,
  1157. skip_gossip: false,
  1158. shred_version: Some(42),
  1159. allow_private_addr: false,
  1160. num_gen_threads: 1,
  1161. transaction_params: TransactionParams {
  1162. num_signatures: None,
  1163. valid_blockhash: true,
  1164. valid_signatures: true,
  1165. unique_transactions: true,
  1166. transaction_type: Some(TransactionType::AccountCreation),
  1167. num_instructions: None,
  1168. },
  1169. tpu_use_quic,
  1170. send_batch_size: TEST_SEND_BATCH_SIZE,
  1171. },
  1172. );
  1173. }
  1174. #[test]
  1175. fn test_dos_with_blockhash_and_payer() {
  1176. run_dos_with_blockhash_and_payer(/*tpu_use_quic*/ false)
  1177. }
  1178. #[test]
  1179. fn test_dos_with_blockhash_and_payer_and_quic() {
  1180. run_dos_with_blockhash_and_payer(/*tpu_use_quic*/ true)
  1181. }
  1182. }