refactor cmdline interface
This commit is contained in:
parent
a63dee87ec
commit
f5339882cb
|
@ -10,7 +10,7 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
clap = "2.33.1"
|
clap = {version = "3.1.5", features = ["derive", "cargo"]}
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
rand = "0.7.0"
|
rand = "0.7.0"
|
||||||
serde = "1.0.136"
|
serde = "1.0.136"
|
||||||
|
|
531
dos/src/main.rs
531
dos/src/main.rs
|
@ -1,6 +1,6 @@
|
||||||
#![allow(clippy::integer_arithmetic)]
|
#![allow(clippy::integer_arithmetic)]
|
||||||
use {
|
use {
|
||||||
clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg},
|
clap::{crate_description, crate_name, crate_version, ArgEnum, Args, Parser},
|
||||||
log::*,
|
log::*,
|
||||||
rand::{thread_rng, Rng},
|
rand::{thread_rng, Rng},
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
|
@ -33,16 +33,6 @@ fn get_repair_contact(nodes: &[ContactInfo]) -> ContactInfo {
|
||||||
contact
|
contact
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for data_type=transaction
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct TransactionParams {
|
|
||||||
unique_transactions: bool, // use unique transactions
|
|
||||||
num_sign: usize, // number of signatures in a transaction
|
|
||||||
valid_block_hash: bool, // use valid blockhash or random
|
|
||||||
valid_signatures: bool, // use valid signatures or not
|
|
||||||
with_payer: bool, // provide a valid payer
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TransactionGenerator {
|
struct TransactionGenerator {
|
||||||
blockhash: Hash,
|
blockhash: Hash,
|
||||||
last_generated: Instant,
|
last_generated: Instant,
|
||||||
|
@ -60,27 +50,28 @@ impl TransactionGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate(&mut self, payer: &Keypair, rpc_client: &Option<RpcClient>) -> Transaction {
|
fn generate(&mut self, payer: Option<&Keypair>, rpc_client: &Option<RpcClient>) -> Transaction {
|
||||||
if !self.transaction_params.unique_transactions && self.cached_transaction != None {
|
if !self.transaction_params.unique_transactions && self.cached_transaction.is_some() {
|
||||||
return self.cached_transaction.as_ref().unwrap().clone();
|
return self.cached_transaction.as_ref().unwrap().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate a new blockhash every 1sec
|
// generate a new blockhash every 1sec
|
||||||
if self.transaction_params.valid_block_hash
|
if self.transaction_params.valid_blockhash
|
||||||
&& self.last_generated.elapsed().as_millis() > 1000
|
&& self.last_generated.elapsed().as_millis() > 1000
|
||||||
{
|
{
|
||||||
self.blockhash = rpc_client.as_ref().unwrap().get_latest_blockhash().unwrap();
|
self.blockhash = rpc_client.as_ref().unwrap().get_latest_blockhash().unwrap();
|
||||||
self.last_generated = Instant::now();
|
self.last_generated = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create an arbitrary valid instructions
|
||||||
let lamports = 5;
|
let lamports = 5;
|
||||||
let transfer_instruction = SystemInstruction::Transfer { lamports };
|
let transfer_instruction = SystemInstruction::Transfer { lamports };
|
||||||
let program_ids = vec![system_program::id(), stake::program::id()];
|
let program_ids = vec![system_program::id(), stake::program::id()];
|
||||||
|
|
||||||
// transaction with payer, in this case signatures are valid and num_sign is irrelevant
|
// transaction with payer, in this case signatures are valid and num_sign is irrelevant
|
||||||
// random payer will cause error "attempt to debit an account but found not record of a prior credit"
|
// random payer will cause error "attempt to debit an account but found no record of a prior credit"
|
||||||
// if payer is correct, it will trigger error with not enough signatures
|
// if payer is correct, it will trigger error with not enough signatures
|
||||||
let transaction = if self.transaction_params.with_payer {
|
let transaction = if let Some(payer) = payer {
|
||||||
let instruction = Instruction::new_with_bincode(
|
let instruction = Instruction::new_with_bincode(
|
||||||
program_ids[0],
|
program_ids[0],
|
||||||
&transfer_instruction,
|
&transfer_instruction,
|
||||||
|
@ -96,8 +87,8 @@ impl TransactionGenerator {
|
||||||
self.blockhash,
|
self.blockhash,
|
||||||
)
|
)
|
||||||
} else if self.transaction_params.valid_signatures {
|
} else if self.transaction_params.valid_signatures {
|
||||||
// this way it wil end up filtered at legacy.rs#L217 (banking_stage)
|
// Since we don't provide a payer, this transaction will
|
||||||
// with error "a program cannot be payer"
|
// end up filtered at legacy.rs#L217 (banking_stage) with error "a program cannot be payer"
|
||||||
let kpvals: Vec<Keypair> = (0..self.transaction_params.num_sign)
|
let kpvals: Vec<Keypair> = (0..self.transaction_params.num_sign)
|
||||||
.map(|_| Keypair::new())
|
.map(|_| Keypair::new())
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -136,7 +127,7 @@ impl TransactionGenerator {
|
||||||
tx
|
tx
|
||||||
};
|
};
|
||||||
|
|
||||||
// if we need to generate only ony transaction, we cache it to reuse later
|
// if we need to generate only one transaction, we cache it to reuse later
|
||||||
if !self.transaction_params.unique_transactions {
|
if !self.transaction_params.unique_transactions {
|
||||||
self.cached_transaction = Some(transaction.clone());
|
self.cached_transaction = Some(transaction.clone());
|
||||||
}
|
}
|
||||||
|
@ -146,50 +137,43 @@ impl TransactionGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_dos(
|
fn run_dos(
|
||||||
payer: &Keypair,
|
|
||||||
nodes: &[ContactInfo],
|
nodes: &[ContactInfo],
|
||||||
iterations: usize,
|
iterations: usize,
|
||||||
entrypoint_addr: SocketAddr,
|
payer: Option<&Keypair>,
|
||||||
data_type: String,
|
params: DosClientParameters,
|
||||||
data_size: usize,
|
|
||||||
mode: String,
|
|
||||||
data_input: Option<String>,
|
|
||||||
transaction_params: Option<TransactionParams>,
|
|
||||||
) {
|
) {
|
||||||
let mut target = None;
|
let mut target = None;
|
||||||
let mut rpc_client = None;
|
let mut rpc_client = None;
|
||||||
if nodes.is_empty() {
|
if nodes.is_empty() {
|
||||||
if mode == "rpc" {
|
if params.mode == Mode::Rpc {
|
||||||
rpc_client = Some(RpcClient::new_socket(entrypoint_addr));
|
rpc_client = Some(RpcClient::new_socket(params.entrypoint_addr));
|
||||||
}
|
}
|
||||||
target = Some(entrypoint_addr);
|
target = Some(params.entrypoint_addr);
|
||||||
} else {
|
} else {
|
||||||
info!("************ NODE ***********");
|
info!("************ NODE ***********");
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
info!("{:?}", node);
|
info!("{:?}", node);
|
||||||
}
|
}
|
||||||
info!("ADDR = {}", entrypoint_addr);
|
info!("ADDR = {}", params.entrypoint_addr);
|
||||||
|
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
//let node = &nodes[1];
|
if node.gossip == params.entrypoint_addr {
|
||||||
if node.gossip == entrypoint_addr {
|
|
||||||
info!("{}", node.gossip);
|
info!("{}", node.gossip);
|
||||||
target = match mode.as_str() {
|
target = match params.mode {
|
||||||
"gossip" => Some(node.gossip),
|
Mode::Gossip => Some(node.gossip),
|
||||||
"tvu" => Some(node.tvu),
|
Mode::Tvu => Some(node.tvu),
|
||||||
"tvu_forwards" => Some(node.tvu_forwards),
|
Mode::TvuForwards => Some(node.tvu_forwards),
|
||||||
"tpu" => {
|
Mode::Tpu => {
|
||||||
rpc_client = Some(RpcClient::new_socket(node.rpc));
|
rpc_client = Some(RpcClient::new_socket(node.rpc));
|
||||||
Some(node.tpu)
|
Some(node.tpu)
|
||||||
}
|
}
|
||||||
"tpu_forwards" => Some(node.tpu_forwards),
|
Mode::TpuForwards => Some(node.tpu_forwards),
|
||||||
"repair" => Some(node.repair),
|
Mode::Repair => Some(node.repair),
|
||||||
"serve_repair" => Some(node.serve_repair),
|
Mode::ServeRepair => Some(node.serve_repair),
|
||||||
"rpc" => {
|
Mode::Rpc => {
|
||||||
rpc_client = Some(RpcClient::new_socket(node.rpc));
|
rpc_client = Some(RpcClient::new_socket(node.rpc));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
&_ => panic!("Unknown mode"),
|
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -201,81 +185,76 @@ fn run_dos(
|
||||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||||
|
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
let mut trans_gen = None;
|
let mut transaction_generator = None;
|
||||||
|
|
||||||
match data_type.as_str() {
|
match params.data_type {
|
||||||
"repair_highest" => {
|
DataType::RepairHighest => {
|
||||||
let slot = 100;
|
let slot = 100;
|
||||||
let req = RepairProtocol::WindowIndexWithNonce(get_repair_contact(nodes), slot, 0, 0);
|
let req = RepairProtocol::WindowIndexWithNonce(get_repair_contact(nodes), slot, 0, 0);
|
||||||
data = bincode::serialize(&req).unwrap();
|
data = bincode::serialize(&req).unwrap();
|
||||||
}
|
}
|
||||||
"repair_shred" => {
|
DataType::RepairShred => {
|
||||||
let slot = 100;
|
let slot = 100;
|
||||||
let req =
|
let req =
|
||||||
RepairProtocol::HighestWindowIndexWithNonce(get_repair_contact(nodes), slot, 0, 0);
|
RepairProtocol::HighestWindowIndexWithNonce(get_repair_contact(nodes), slot, 0, 0);
|
||||||
data = bincode::serialize(&req).unwrap();
|
data = bincode::serialize(&req).unwrap();
|
||||||
}
|
}
|
||||||
"repair_orphan" => {
|
DataType::RepairOrphan => {
|
||||||
let slot = 100;
|
let slot = 100;
|
||||||
let req = RepairProtocol::OrphanWithNonce(get_repair_contact(nodes), slot, 0);
|
let req = RepairProtocol::OrphanWithNonce(get_repair_contact(nodes), slot, 0);
|
||||||
data = bincode::serialize(&req).unwrap();
|
data = bincode::serialize(&req).unwrap();
|
||||||
}
|
}
|
||||||
"random" => {
|
DataType::Random => {
|
||||||
data.resize(data_size, 0);
|
data.resize(params.data_size, 0);
|
||||||
}
|
}
|
||||||
"transaction" => {
|
DataType::Transaction => {
|
||||||
if transaction_params.is_none() {
|
let tp = params.transaction_params;
|
||||||
panic!("transaction parameters are not specified");
|
|
||||||
}
|
|
||||||
let tp = transaction_params.unwrap();
|
|
||||||
info!("{:?}", tp);
|
info!("{:?}", tp);
|
||||||
|
|
||||||
trans_gen = Some(TransactionGenerator::new(tp));
|
transaction_generator = Some(TransactionGenerator::new(tp));
|
||||||
let tx = trans_gen.as_mut().unwrap().generate(payer, &rpc_client);
|
let tx = transaction_generator
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.generate(payer, &rpc_client);
|
||||||
info!("{:?}", tx);
|
info!("{:?}", tx);
|
||||||
data = bincode::serialize(&tx).unwrap();
|
data = bincode::serialize(&tx).unwrap();
|
||||||
}
|
}
|
||||||
"get_account_info" => {}
|
DataType::GetAccountInfo => {}
|
||||||
"get_program_accounts" => {}
|
DataType::GetProgramAccounts => {}
|
||||||
&_ => {
|
|
||||||
panic!("unknown data type");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("TARGET = {}, NODE = {}", target, nodes[1].rpc);
|
info!("TARGET = {}, NODE = {}", target, nodes[1].rpc);
|
||||||
|
|
||||||
let mut last_log = Instant::now();
|
let mut last_log = Instant::now();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let mut error_count = 0;
|
let mut error_count = 0;
|
||||||
loop {
|
loop {
|
||||||
if mode == "rpc" {
|
if params.mode == Mode::Rpc {
|
||||||
match data_type.as_str() {
|
match params.data_type {
|
||||||
"get_account_info" => {
|
DataType::GetAccountInfo => {
|
||||||
let res = rpc_client
|
let res = rpc_client.as_ref().unwrap().get_account(
|
||||||
.as_ref()
|
&Pubkey::from_str(params.data_input.as_ref().unwrap()).unwrap(),
|
||||||
.unwrap()
|
|
||||||
.get_account(&Pubkey::from_str(data_input.as_ref().unwrap()).unwrap());
|
|
||||||
if res.is_err() {
|
|
||||||
error_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"get_program_accounts" => {
|
|
||||||
let res = rpc_client.as_ref().unwrap().get_program_accounts(
|
|
||||||
&Pubkey::from_str(data_input.as_ref().unwrap()).unwrap(),
|
|
||||||
);
|
);
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
error_count += 1;
|
error_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&_ => {
|
DataType::GetProgramAccounts => {
|
||||||
|
let res = rpc_client.as_ref().unwrap().get_program_accounts(
|
||||||
|
&Pubkey::from_str(params.data_input.as_ref().unwrap()).unwrap(),
|
||||||
|
);
|
||||||
|
if res.is_err() {
|
||||||
|
error_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
panic!("unsupported data type");
|
panic!("unsupported data type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if data_type == "random" {
|
if params.data_type == DataType::Random {
|
||||||
thread_rng().fill(&mut data[..]);
|
thread_rng().fill(&mut data[..]);
|
||||||
}
|
}
|
||||||
if let Some(tg) = trans_gen.as_mut() {
|
if let Some(tg) = transaction_generator.as_mut() {
|
||||||
let tx = tg.generate(payer, &rpc_client);
|
let tx = tg.generate(payer, &rpc_client);
|
||||||
info!("{:?}", tx);
|
info!("{:?}", tx);
|
||||||
data = bincode::serialize(&tx).unwrap();
|
data = bincode::serialize(&tx).unwrap();
|
||||||
|
@ -297,24 +276,20 @@ fn run_dos(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
// command line parsing
|
||||||
solana_logger::setup_with_default("solana=info");
|
#[derive(Parser)]
|
||||||
let matches = App::new(crate_name!())
|
#[clap(name = crate_name!(), version = crate_version!(), about = crate_description!())]
|
||||||
.about(crate_description!())
|
struct DosClientParameters {
|
||||||
.version(solana_version::version!())
|
#[clap(
|
||||||
.arg(
|
long = "entrypoint",
|
||||||
Arg::with_name("entrypoint")
|
parse(try_from_str = addr_parser),
|
||||||
.long("entrypoint")
|
default_value = "127.0.0.1:8001",
|
||||||
.takes_value(true)
|
help = "Gossip entrypoint address. Usually <ip>:8001"
|
||||||
.value_name("HOST:PORT")
|
)]
|
||||||
.help("Gossip entrypoint address. Usually <ip>:8001"),
|
entrypoint_addr: SocketAddr,
|
||||||
)
|
|
||||||
.arg(
|
#[clap(long="mode",
|
||||||
Arg::with_name("mode")
|
possible_values=&[
|
||||||
.long("mode")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("MODE")
|
|
||||||
.possible_values(&[
|
|
||||||
"gossip",
|
"gossip",
|
||||||
"tvu",
|
"tvu",
|
||||||
"tvu_forwards",
|
"tvu_forwards",
|
||||||
|
@ -323,155 +298,197 @@ fn main() {
|
||||||
"repair",
|
"repair",
|
||||||
"serve_repair",
|
"serve_repair",
|
||||||
"rpc",
|
"rpc",
|
||||||
])
|
],
|
||||||
.help("Interface to DoS"),
|
parse(try_from_str = mode_parser),
|
||||||
)
|
help="Interface to DoS")]
|
||||||
.arg(
|
mode: Mode,
|
||||||
Arg::with_name("data_size")
|
|
||||||
.long("data-size")
|
#[clap(
|
||||||
.takes_value(true)
|
long = "data-size",
|
||||||
.value_name("BYTES")
|
default_value = "128",
|
||||||
.help("Size of packet to DoS with"),
|
required_if_eq("data-type", "random"),
|
||||||
)
|
help = "Size of packet to DoS with, relevant only for data-type=random"
|
||||||
.arg(
|
)]
|
||||||
Arg::with_name("data_type")
|
data_size: usize,
|
||||||
.long("data-type")
|
|
||||||
.takes_value(true)
|
#[clap(long="data-type",
|
||||||
.value_name("TYPE")
|
possible_values=&[
|
||||||
.possible_values(&[
|
|
||||||
"repair_highest",
|
"repair_highest",
|
||||||
"repair_shred",
|
"repair_shred",
|
||||||
"repair_orphan",
|
"repair_orphan",
|
||||||
"random",
|
"random",
|
||||||
"get_account_info",
|
"get_account_info",
|
||||||
"get_program_accounts",
|
"get_program_accounts",
|
||||||
"transaction",
|
"transaction"],
|
||||||
])
|
parse(try_from_str = data_type_parser), help="Type of data to send")]
|
||||||
.help("Type of data to send"),
|
data_type: DataType,
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("data_input")
|
|
||||||
.long("data-input")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("TYPE")
|
|
||||||
.help("Data to send"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("skip_gossip")
|
|
||||||
.long("skip-gossip")
|
|
||||||
.help("Just use entrypoint address directly"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("allow_private_addr")
|
|
||||||
.long("allow-private-addr")
|
|
||||||
.takes_value(false)
|
|
||||||
.help("Allow contacting private ip addresses")
|
|
||||||
.hidden(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("num_sign")
|
|
||||||
.long("number-of-signatures")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("Number of signatures in transaction"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("valid_blockhash")
|
|
||||||
.long("generate-valid-blockhash")
|
|
||||||
.takes_value(false)
|
|
||||||
.help("Generate a valid blockhash for transaction")
|
|
||||||
.hidden(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("valid_sign")
|
|
||||||
.long("generate-valid-signatures")
|
|
||||||
.takes_value(false)
|
|
||||||
.help("Generate valid signature(s) for transaction")
|
|
||||||
.hidden(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("unique_trans")
|
|
||||||
.long("generate-unique-transactions")
|
|
||||||
.takes_value(false)
|
|
||||||
.help("Generate unique transaction")
|
|
||||||
.hidden(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("payer")
|
|
||||||
.long("payer")
|
|
||||||
.takes_value(false)
|
|
||||||
.value_name("FILE")
|
|
||||||
.help("Payer's keypair to fund transactions")
|
|
||||||
.hidden(true),
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let mut entrypoint_addr = SocketAddr::from(([127, 0, 0, 1], 8001));
|
#[clap(long = "data-input", help = "Data to send [Optional]")]
|
||||||
if let Some(addr) = matches.value_of("entrypoint") {
|
data_input: Option<String>,
|
||||||
entrypoint_addr = solana_net_utils::parse_host_port(addr).unwrap_or_else(|e| {
|
|
||||||
eprintln!("failed to parse entrypoint address: {}", e);
|
#[clap(long = "skip-gossip", help = "Just use entrypoint address directly")]
|
||||||
exit(1)
|
skip_gossip: bool,
|
||||||
});
|
|
||||||
|
#[clap(
|
||||||
|
long = "allow-private-addr",
|
||||||
|
help = "Allow contacting private ip addresses"
|
||||||
|
)]
|
||||||
|
allow_private_addr: bool,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
transaction_params: TransactionParams,
|
||||||
}
|
}
|
||||||
let data_size = value_t!(matches, "data_size", usize).unwrap_or(128);
|
|
||||||
let skip_gossip = matches.is_present("skip_gossip");
|
|
||||||
|
|
||||||
let mode = value_t_or_exit!(matches, "mode", String);
|
#[derive(Args, Serialize, Deserialize, Debug, Default)]
|
||||||
let data_type = value_t_or_exit!(matches, "data_type", String);
|
struct TransactionParams {
|
||||||
let data_input = value_t!(matches, "data_input", String).ok();
|
#[clap(
|
||||||
|
long = "num-sign",
|
||||||
|
default_value = "2",
|
||||||
|
help = "Number of signatures in transaction"
|
||||||
|
)]
|
||||||
|
num_sign: usize,
|
||||||
|
|
||||||
let transaction_params = match data_type.as_str() {
|
#[clap(
|
||||||
"transaction" => Some(TransactionParams {
|
long = "valid-blockhash",
|
||||||
unique_transactions: matches.is_present("unique_trans"),
|
help = "Generate a valid blockhash for transaction"
|
||||||
num_sign: value_t!(matches, "num_sign", usize).unwrap_or(2),
|
)]
|
||||||
valid_block_hash: matches.is_present("valid_blockhash"),
|
valid_blockhash: bool,
|
||||||
valid_signatures: matches.is_present("valid_sign"),
|
|
||||||
with_payer: matches.is_present("payer"),
|
#[clap(
|
||||||
}),
|
long = "valid-signatures",
|
||||||
_ => None,
|
help = "Generate valid signature(s) for transaction"
|
||||||
};
|
)]
|
||||||
|
valid_signatures: bool,
|
||||||
|
|
||||||
|
#[clap(long = "unique-transactions", help = "Generate unique transactions")]
|
||||||
|
unique_transactions: bool,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long = "payer",
|
||||||
|
help = "Payer's keypair file to fund transactions [Optional]"
|
||||||
|
)]
|
||||||
|
payer_filename: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ArgEnum, Clone, Eq, PartialEq)]
|
||||||
|
enum Mode {
|
||||||
|
Gossip,
|
||||||
|
Tvu,
|
||||||
|
TvuForwards,
|
||||||
|
Tpu,
|
||||||
|
TpuForwards,
|
||||||
|
Repair,
|
||||||
|
ServeRepair,
|
||||||
|
Rpc,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ArgEnum, Clone, Eq, PartialEq)]
|
||||||
|
enum DataType {
|
||||||
|
RepairHighest,
|
||||||
|
RepairShred,
|
||||||
|
RepairOrphan,
|
||||||
|
Random,
|
||||||
|
GetAccountInfo,
|
||||||
|
GetProgramAccounts,
|
||||||
|
Transaction,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 entrypoint address"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_type_parser(s: &str) -> Result<DataType, &'static str> {
|
||||||
|
match s {
|
||||||
|
"repair_highest" => Ok(DataType::RepairHighest),
|
||||||
|
"repair_shred" => Ok(DataType::RepairShred),
|
||||||
|
"repair_orphan" => Ok(DataType::RepairOrphan),
|
||||||
|
"random" => Ok(DataType::Random),
|
||||||
|
"get_account_info" => Ok(DataType::GetAccountInfo),
|
||||||
|
"get_program_accounts" => Ok(DataType::GetProgramAccounts),
|
||||||
|
"transaction" => Ok(DataType::Transaction),
|
||||||
|
_ => Err("unsupported value"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mode_parser(s: &str) -> Result<Mode, &'static str> {
|
||||||
|
match s {
|
||||||
|
"gossip" => Ok(Mode::Gossip),
|
||||||
|
"tvu" => Ok(Mode::Tvu),
|
||||||
|
"tvu_forwards" => Ok(Mode::TvuForwards),
|
||||||
|
"tpu" => Ok(Mode::Tpu),
|
||||||
|
"tpu_forwards" => Ok(Mode::TpuForwards),
|
||||||
|
"repair" => Ok(Mode::Repair),
|
||||||
|
"serve_repair" => Ok(Mode::ServeRepair),
|
||||||
|
"rpc" => Ok(Mode::Rpc),
|
||||||
|
_ => Err("unsupported value"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
{
|
||||||
|
panic!("unsupported data type");
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.data_type != DataType::Transaction {
|
||||||
|
let tp = ¶ms.transaction_params;
|
||||||
|
if tp.valid_blockhash
|
||||||
|
|| tp.valid_signatures
|
||||||
|
|| tp.unique_transactions
|
||||||
|
|| tp.payer_filename.is_some()
|
||||||
|
{
|
||||||
|
println!("Arguments valid-blockhash, valid-sign, unique-trans, payer are ignored if data-type != transaction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
solana_logger::setup_with_default("solana=info");
|
||||||
|
let cmd_params = DosClientParameters::parse();
|
||||||
|
validate_input(&cmd_params);
|
||||||
|
|
||||||
let mut nodes = vec![];
|
let mut nodes = vec![];
|
||||||
if !skip_gossip {
|
if !cmd_params.skip_gossip {
|
||||||
info!("Finding cluster entry: {:?}", entrypoint_addr);
|
info!("Finding cluster entry: {:?}", cmd_params.entrypoint_addr);
|
||||||
let socket_addr_space = SocketAddrSpace::new(matches.is_present("allow_private_addr"));
|
let socket_addr_space = SocketAddrSpace::new(cmd_params.allow_private_addr);
|
||||||
let (gossip_nodes, _validators) = discover(
|
let (gossip_nodes, _validators) = discover(
|
||||||
None, // keypair
|
None, // keypair
|
||||||
Some(&entrypoint_addr),
|
Some(&cmd_params.entrypoint_addr),
|
||||||
None, // num_nodes
|
None, // num_nodes
|
||||||
Duration::from_secs(60), // timeout
|
Duration::from_secs(60), // timeout
|
||||||
None, // find_node_by_pubkey
|
None, // find_node_by_pubkey
|
||||||
Some(&entrypoint_addr), // find_node_by_gossip_addr
|
Some(&cmd_params.entrypoint_addr), // find_node_by_gossip_addr
|
||||||
None, // my_gossip_addr
|
None, // my_gossip_addr
|
||||||
0, // my_shred_version
|
0, // my_shred_version
|
||||||
socket_addr_space,
|
socket_addr_space,
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
eprintln!("Failed to discover {} node: {:?}", entrypoint_addr, err);
|
eprintln!(
|
||||||
|
"Failed to discover {} node: {:?}",
|
||||||
|
cmd_params.entrypoint_addr, err
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
});
|
});
|
||||||
nodes = gossip_nodes;
|
nodes = gossip_nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
let payer = if transaction_params.is_some() && transaction_params.as_ref().unwrap().with_payer {
|
info!("done found {} nodes", nodes.len());
|
||||||
let keypair_file_name = value_t_or_exit!(matches, "payer", String);
|
let payer = cmd_params
|
||||||
|
.transaction_params
|
||||||
|
.payer_filename
|
||||||
|
.as_ref()
|
||||||
|
.map(|keypair_file_name| {
|
||||||
read_keypair_file(&keypair_file_name)
|
read_keypair_file(&keypair_file_name)
|
||||||
.unwrap_or_else(|_| panic!("bad keypair {:?}", keypair_file_name))
|
.unwrap_or_else(|_| panic!("bad keypair {:?}", keypair_file_name))
|
||||||
} else {
|
});
|
||||||
Keypair::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
info!("done found {} nodes", nodes.len());
|
run_dos(&nodes, 0, payer.as_ref(), cmd_params);
|
||||||
run_dos(
|
|
||||||
&payer,
|
|
||||||
&nodes,
|
|
||||||
0,
|
|
||||||
entrypoint_addr,
|
|
||||||
data_type,
|
|
||||||
data_size,
|
|
||||||
mode,
|
|
||||||
data_input,
|
|
||||||
transaction_params,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -490,42 +507,52 @@ pub mod test {
|
||||||
)];
|
)];
|
||||||
let entrypoint_addr = nodes[0].gossip;
|
let entrypoint_addr = nodes[0].gossip;
|
||||||
|
|
||||||
let payer = Keypair::new();
|
|
||||||
|
|
||||||
run_dos(
|
run_dos(
|
||||||
&payer,
|
|
||||||
&nodes,
|
&nodes,
|
||||||
1,
|
1,
|
||||||
|
None,
|
||||||
|
DosClientParameters {
|
||||||
entrypoint_addr,
|
entrypoint_addr,
|
||||||
"random".to_string(),
|
mode: Mode::Tvu,
|
||||||
10,
|
data_size: 10,
|
||||||
"tvu".to_string(),
|
data_type: DataType::Random,
|
||||||
None,
|
data_input: None,
|
||||||
None,
|
skip_gossip: false,
|
||||||
|
allow_private_addr: false,
|
||||||
|
transaction_params: TransactionParams::default(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
run_dos(
|
run_dos(
|
||||||
&payer,
|
|
||||||
&nodes,
|
&nodes,
|
||||||
1,
|
1,
|
||||||
|
None,
|
||||||
|
DosClientParameters {
|
||||||
entrypoint_addr,
|
entrypoint_addr,
|
||||||
"repair_highest".to_string(),
|
mode: Mode::Repair,
|
||||||
10,
|
data_size: 10,
|
||||||
"repair".to_string(),
|
data_type: DataType::RepairHighest,
|
||||||
None,
|
data_input: None,
|
||||||
None,
|
skip_gossip: false,
|
||||||
|
allow_private_addr: false,
|
||||||
|
transaction_params: TransactionParams::default(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
run_dos(
|
run_dos(
|
||||||
&payer,
|
|
||||||
&nodes,
|
&nodes,
|
||||||
1,
|
1,
|
||||||
|
None,
|
||||||
|
DosClientParameters {
|
||||||
entrypoint_addr,
|
entrypoint_addr,
|
||||||
"repair_shred".to_string(),
|
mode: Mode::ServeRepair,
|
||||||
10,
|
data_size: 10,
|
||||||
"serve_repair".to_string(),
|
data_type: DataType::RepairShred,
|
||||||
None,
|
data_input: None,
|
||||||
None,
|
skip_gossip: false,
|
||||||
|
allow_private_addr: false,
|
||||||
|
transaction_params: TransactionParams::default(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,24 +568,26 @@ pub mod test {
|
||||||
let nodes = cluster.get_node_pubkeys();
|
let nodes = cluster.get_node_pubkeys();
|
||||||
let node = cluster.get_contact_info(&nodes[0]).unwrap().clone();
|
let node = cluster.get_contact_info(&nodes[0]).unwrap().clone();
|
||||||
|
|
||||||
let tp = Some(TransactionParams {
|
|
||||||
unique_transactions: true,
|
|
||||||
num_sign: 2,
|
|
||||||
valid_block_hash: true, // use valid blockhash or random
|
|
||||||
valid_signatures: true, // use valid signatures or not
|
|
||||||
with_payer: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
run_dos(
|
run_dos(
|
||||||
&cluster.funding_keypair,
|
|
||||||
&[node],
|
&[node],
|
||||||
10_000_000,
|
10_000_000,
|
||||||
cluster.entry_point_info.gossip,
|
Some(&cluster.funding_keypair),
|
||||||
"transaction".to_string(),
|
DosClientParameters {
|
||||||
1000,
|
entrypoint_addr: cluster.entry_point_info.gossip,
|
||||||
"tpu".to_string(),
|
mode: Mode::Tpu,
|
||||||
None,
|
data_size: 0, // irrelevant if not random
|
||||||
tp,
|
data_type: DataType::Transaction,
|
||||||
|
data_input: None,
|
||||||
|
skip_gossip: false,
|
||||||
|
allow_private_addr: false,
|
||||||
|
transaction_params: TransactionParams {
|
||||||
|
num_sign: 2,
|
||||||
|
valid_blockhash: true,
|
||||||
|
valid_signatures: true,
|
||||||
|
unique_transactions: true,
|
||||||
|
payer_filename: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue