| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- use {
- clap::{crate_description, crate_name, crate_version, ArgEnum, Args, Parser},
- serde::{Deserialize, Serialize},
- solana_pubkey::Pubkey,
- std::{net::SocketAddr, process::exit, str::FromStr},
- };
- #[derive(Parser, Debug, PartialEq, Eq)]
- #[clap(name = crate_name!(),
- version = crate_version!(),
- about = crate_description!(),
- rename_all = "kebab-case"
- )]
- pub struct DosClientParameters {
- #[clap(long, arg_enum, help = "Interface to DoS")]
- pub mode: Mode,
- #[clap(long, arg_enum, help = "Type of data to send")]
- pub data_type: DataType,
- #[clap(
- long = "entrypoint",
- parse(try_from_str = addr_parser),
- default_value = "127.0.0.1:8001",
- help = "Gossip entrypoint address. Usually <ip>:8001"
- )]
- pub entrypoint_addr: SocketAddr,
- #[clap(
- long,
- default_value = "128",
- required_if_eq("data-type", "random"),
- help = "Size of packet to DoS with, relevant only for data-type=random"
- )]
- pub data_size: usize,
- #[clap(
- long,
- parse(try_from_str = pubkey_parser),
- required_if_eq("mode", "rpc"),
- help = "Pubkey for rpc-mode calls"
- )]
- pub data_input: Option<Pubkey>,
- #[clap(long, help = "Just use entrypoint address directly")]
- pub skip_gossip: bool,
- #[clap(
- long,
- conflicts_with("skip-gossip"),
- help = "The shred version to use for gossip discovery. If not provided, will be \
- discovered from the network"
- )]
- pub shred_version: Option<u16>,
- #[clap(long, help = "Allow contacting private ip addresses")]
- pub allow_private_addr: bool,
- #[clap(
- long,
- default_value = "1",
- help = "Number of threads generating transactions"
- )]
- pub num_gen_threads: usize,
- #[clap(flatten)]
- pub transaction_params: TransactionParams,
- #[clap(
- long,
- conflicts_with("skip-gossip"),
- help = "Submit transactions via QUIC"
- )]
- pub tpu_use_quic: bool,
- #[clap(long, default_value = "16384", help = "Size of the transactions batch")]
- pub send_batch_size: usize,
- }
- #[derive(Args, Clone, Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
- #[clap(rename_all = "kebab-case")]
- pub struct TransactionParams {
- #[clap(
- long,
- conflicts_with("valid-blockhash"),
- help = "Number of signatures in transaction"
- )]
- pub num_signatures: Option<usize>,
- #[clap(
- long,
- requires("transaction-type"),
- conflicts_with("skip-gossip"),
- help = "Generate a valid blockhash for transaction"
- )]
- pub valid_blockhash: bool,
- #[clap(
- long,
- requires("num-signatures"),
- help = "Generate valid signature(s) for transaction"
- )]
- pub valid_signatures: bool,
- #[clap(long, help = "Generate unique transactions")]
- pub unique_transactions: bool,
- #[clap(
- long,
- arg_enum,
- requires("valid-blockhash"),
- help = "Type of transaction to be sent"
- )]
- pub transaction_type: Option<TransactionType>,
- #[clap(
- long,
- required_if_eq("transaction-type", "transfer"),
- help = "Number of instructions in transfer transaction"
- )]
- pub num_instructions: Option<usize>,
- }
- #[derive(ArgEnum, Clone, Copy, Debug, Eq, PartialEq)]
- pub enum Mode {
- Gossip,
- Tvu,
- Tpu,
- TpuForwards,
- Repair,
- ServeRepair,
- Rpc,
- }
- #[derive(ArgEnum, Clone, Copy, Debug, Eq, PartialEq)]
- pub enum DataType {
- RepairHighest,
- RepairShred,
- RepairOrphan,
- Random,
- GetAccountInfo,
- GetProgramAccounts,
- Transaction,
- }
- #[derive(ArgEnum, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
- pub enum TransactionType {
- Transfer,
- AccountCreation,
- }
- fn addr_parser(addr: &str) -> Result<SocketAddr, &'static str> {
- match solana_net_utils::parse_host_port(addr) {
- Ok(v) => Ok(v),
- Err(_) => Err("failed to parse address"),
- }
- }
- fn pubkey_parser(pubkey: &str) -> Result<Pubkey, &'static str> {
- match Pubkey::from_str(pubkey) {
- Ok(v) => Ok(v),
- Err(_) => Err("failed to parse pubkey"),
- }
- }
- /// input checks which are not covered by Clap
- fn validate_input(params: &DosClientParameters) {
- if params.mode == Mode::Rpc
- && (params.data_type != DataType::GetAccountInfo
- && params.data_type != DataType::GetProgramAccounts)
- {
- eprintln!("unsupported data type");
- exit(1);
- }
- if params.data_type != DataType::Transaction {
- let tp = ¶ms.transaction_params;
- if tp.valid_blockhash || tp.valid_signatures || tp.unique_transactions {
- eprintln!(
- "Arguments valid-blockhash, valid-sign, unique-transactions are ignored if \
- data-type != transaction"
- );
- exit(1);
- }
- }
- }
- pub fn build_cli_parameters() -> DosClientParameters {
- let cmd_params = DosClientParameters::parse();
- validate_input(&cmd_params);
- cmd_params
- }
- #[cfg(test)]
- mod tests {
- use {super::*, clap::Parser, solana_pubkey::Pubkey};
- #[test]
- fn test_cli_parse_rpc_no_data_input() {
- let result = DosClientParameters::try_parse_from(vec![
- "solana-dos",
- "--mode",
- "rpc",
- "--data-type",
- "get-account-info",
- //--data-input is required for `--mode rpc` but it is not specified
- ]);
- assert!(result.is_err());
- assert_eq!(
- result.unwrap_err().kind(),
- clap::error::ErrorKind::MissingRequiredArgument
- );
- }
- #[test]
- fn test_cli_parse_rpc_data_input() {
- let entrypoint_addr: SocketAddr = "127.0.0.1:8001".parse().unwrap();
- let pubkey = Pubkey::default();
- let pubkey_str: String = pubkey.to_string();
- let params = DosClientParameters::try_parse_from(vec![
- "solana-dos",
- "--mode",
- "rpc",
- "--data-type",
- "get-account-info",
- "--data-input",
- &pubkey_str,
- "--shred-version",
- "42",
- ])
- .unwrap();
- assert_eq!(
- params,
- DosClientParameters {
- entrypoint_addr,
- mode: Mode::Rpc,
- data_size: 128, // default value
- data_type: DataType::GetAccountInfo,
- data_input: Some(pubkey),
- skip_gossip: false,
- shred_version: Some(42),
- allow_private_addr: false,
- transaction_params: TransactionParams::default(),
- tpu_use_quic: false,
- num_gen_threads: 1,
- send_batch_size: 16384,
- },
- );
- }
- #[test]
- fn test_cli_parse_dos_valid_signatures() {
- let entrypoint_addr: SocketAddr = "127.0.0.1:8001".parse().unwrap();
- let params = DosClientParameters::try_parse_from(vec![
- "solana-dos",
- "--mode",
- "tpu",
- "--data-type",
- "transaction",
- "--unique-transactions",
- "--valid-signatures",
- "--num-signatures",
- "8",
- "--tpu-use-quic",
- "--send-batch-size",
- "1",
- "--shred-version",
- "42",
- ])
- .unwrap();
- assert_eq!(
- params,
- DosClientParameters {
- entrypoint_addr,
- mode: Mode::Tpu,
- data_size: 128,
- data_type: DataType::Transaction,
- data_input: None,
- skip_gossip: false,
- shred_version: Some(42),
- allow_private_addr: false,
- num_gen_threads: 1,
- transaction_params: TransactionParams {
- num_signatures: Some(8),
- valid_blockhash: false,
- valid_signatures: true,
- unique_transactions: true,
- transaction_type: None,
- num_instructions: None,
- },
- tpu_use_quic: true,
- send_batch_size: 1,
- },
- );
- }
- #[test]
- fn test_cli_parse_dos_transfer() {
- let entrypoint_addr: SocketAddr = "127.0.0.1:8001".parse().unwrap();
- let params = DosClientParameters::try_parse_from(vec![
- "solana-dos",
- "--mode",
- "tpu",
- "--data-type",
- "transaction",
- "--unique-transactions",
- "--valid-blockhash",
- "--transaction-type",
- "transfer",
- "--num-instructions",
- "1",
- "--send-batch-size",
- "1",
- "--shred-version",
- "42",
- ])
- .unwrap();
- assert_eq!(
- params,
- DosClientParameters {
- entrypoint_addr,
- mode: Mode::Tpu,
- data_size: 128, // irrelevant if not random
- data_type: DataType::Transaction,
- data_input: None,
- skip_gossip: false,
- shred_version: Some(42),
- allow_private_addr: false,
- num_gen_threads: 1,
- transaction_params: TransactionParams {
- num_signatures: None,
- valid_blockhash: true,
- valid_signatures: false,
- unique_transactions: true,
- transaction_type: Some(TransactionType::Transfer),
- num_instructions: Some(1),
- },
- tpu_use_quic: false,
- send_batch_size: 1,
- },
- );
- let result = DosClientParameters::try_parse_from(vec![
- "solana-dos",
- "--mode",
- "tpu",
- "--data-type",
- "transaction",
- "--unique-transactions",
- "--transaction-type",
- "transfer",
- "--num-instructions",
- "8",
- "--shred-version",
- "42",
- ]);
- assert!(result.is_err());
- assert_eq!(
- result.unwrap_err().kind(),
- clap::error::ErrorKind::MissingRequiredArgument
- );
- let entrypoint_addr: SocketAddr = "127.0.0.1:8001".parse().unwrap();
- let params = DosClientParameters::try_parse_from(vec![
- "solana-dos",
- "--mode",
- "tpu",
- "--data-type",
- "transaction",
- "--unique-transactions",
- "--valid-blockhash",
- "--transaction-type",
- "transfer",
- "--num-instructions",
- "8",
- "--send-batch-size",
- "1",
- "--shred-version",
- "42",
- ])
- .unwrap();
- assert_eq!(
- params,
- DosClientParameters {
- entrypoint_addr,
- mode: Mode::Tpu,
- data_size: 128, // irrelevant if not random
- data_type: DataType::Transaction,
- data_input: None,
- skip_gossip: false,
- shred_version: Some(42),
- allow_private_addr: false,
- num_gen_threads: 1,
- transaction_params: TransactionParams {
- num_signatures: None,
- valid_blockhash: true,
- valid_signatures: false,
- unique_transactions: true,
- transaction_type: Some(TransactionType::Transfer),
- num_instructions: Some(8),
- },
- tpu_use_quic: false,
- send_batch_size: 1,
- },
- );
- }
- #[test]
- fn test_cli_parse_dos_create_account() {
- let entrypoint_addr: SocketAddr = "127.0.0.1:8001".parse().unwrap();
- let params = DosClientParameters::try_parse_from(vec![
- "solana-dos",
- "--mode",
- "tpu",
- "--data-type",
- "transaction",
- "--unique-transactions",
- "--valid-blockhash",
- "--transaction-type",
- "account-creation",
- "--send-batch-size",
- "1",
- "--shred-version",
- "42",
- ])
- .unwrap();
- assert_eq!(
- params,
- DosClientParameters {
- entrypoint_addr,
- mode: Mode::Tpu,
- data_size: 128, // irrelevant if not random
- data_type: DataType::Transaction,
- data_input: None,
- skip_gossip: false,
- shred_version: Some(42),
- allow_private_addr: false,
- num_gen_threads: 1,
- transaction_params: TransactionParams {
- num_signatures: None,
- valid_blockhash: true,
- valid_signatures: false,
- unique_transactions: true,
- transaction_type: Some(TransactionType::AccountCreation),
- num_instructions: None,
- },
- tpu_use_quic: false,
- send_batch_size: 1,
- },
- );
- }
- #[test]
- #[should_panic]
- fn test_cli_parse_dos_conflicting_sign_instruction() {
- // check conflicting args num-signatures and num-instructions
- let result = DosClientParameters::try_parse_from(vec![
- "solana-dos",
- "--mode",
- "tpu",
- "--data-type",
- "transaction",
- "--unique-transactions",
- "--valid-signatures",
- "--num-signatures",
- "8",
- "--num-instructions",
- "1",
- "--shred-version",
- "42",
- ]);
- assert!(result.is_err());
- }
- }
|