#![deny(unsafe_code)]
#![warn(
missing_docs,
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
unused_results
)]
use clap::{value_t, App, Arg};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use safe_app::{AppError, Client, PubImmutableData};
use safe_authenticator::{AuthClient, Authenticator};
use safe_core::btree_map;
use safe_core::utils;
use safe_nd::{ClientFullId, IData, PublicKey, SeqMutableData};
use unwrap::unwrap;
use xor_name::XorName;
fn random_mutable_data<R: Rng>(
type_tag: u64,
public_key: &PublicKey,
rng: &mut R,
) -> SeqMutableData {
SeqMutableData::new_with_data(
XorName(rng.gen()),
type_tag,
btree_map![],
btree_map![],
*public_key,
)
}
enum Data {
Mutable(SeqMutableData),
Immutable(IData),
}
#[tokio::main]
async fn main() {
unwrap!(safe_core::utils::logging::init(true));
let matches = App::new("client_stress_test")
.about(
"A stress test involving putting and getting immutable and mutable data chunks to the \
network",
)
.arg(
Arg::with_name("immutable")
.short("i")
.long("immutable")
.takes_value(true)
.default_value("100")
.help("Number of PubImmutableData chunks to Put and Get."),
)
.arg(
Arg::with_name("mutable")
.short("m")
.long("mutable")
.takes_value(true)
.default_value("100")
.help("Number of MutableData chunks to Put and Get."),
)
.arg(
Arg::with_name("seed")
.long("seed")
.takes_value(true)
.help("Seed for a pseudo-random number generator."),
)
.arg(
Arg::with_name("get-only")
.long("get-only")
.requires("seed")
.help("Only Get the data, don't Put it. Logs in to an existing account."),
)
.arg(
Arg::with_name("locator")
.short("l")
.long("locator")
.takes_value(true)
.requires("password")
.help("Use the given Locator for login."),
)
.arg(
Arg::with_name("password")
.short("p")
.long("password")
.takes_value(true)
.requires("locator")
.help("Use the given Password for login."),
)
.get_matches();
let immutable_data_count = unwrap!(value_t!(matches, "immutable", usize));
let mutable_data_count = unwrap!(value_t!(matches, "mutable", usize));
let seed = if matches.is_present("seed") {
unwrap!(value_t!(matches, "seed", u64))
} else {
rand::random()
};
let mut rng = StdRng::seed_from_u64(seed);
let get_only = matches.is_present("get-only");
let (locator, password) = if let (Some(locator), Some(password)) =
(matches.value_of("locator"), matches.value_of("password"))
{
(locator.to_string(), password.to_string())
} else {
let new_locator = utils::generate_readable_string_rng(&mut rng, 20);
let new_password = utils::generate_readable_string_rng(&mut rng, 20);
println!(
"A new account will be created.\nLocator: {}\nPassword: {}",
new_locator, new_password
);
(new_locator, new_password)
};
let try_login = matches.is_present("locator") && matches.is_present("password");
let auth = if get_only || try_login {
println!("\n\tAccount Login");
println!("\t================");
println!("\nTrying to login to an account ...");
unwrap!(Authenticator::login(locator, password, || ()).await)
} else {
println!("\n\tAccount Creation");
println!("\t================");
println!("\nTrying to create an account ...");
let client_id = ClientFullId::new_bls(&mut rng);
let auth = unwrap!(
Authenticator::create_client_with_acc(
locator.as_str(),
password.as_str(),
client_id,
|| ()
)
.await
);
println!("Account created!");
auth
};
println!("\nLogged in successfully!");
println!("Seed: {}", seed);
let mut stored_data = Vec::with_capacity(mutable_data_count + immutable_data_count);
for _ in 0..immutable_data_count {
let data = PubImmutableData::new(utils::generate_random_vector_rng(&mut rng, 1024));
println!("{:?}", data.name());
stored_data.push(Data::Immutable(data.into()));
}
let public_key = auth.client.public_key().await;
for _ in immutable_data_count..(immutable_data_count + mutable_data_count) {
let mutable_data = random_mutable_data(100_000, &public_key, &mut rng);
stored_data.push(Data::Mutable(mutable_data));
}
let message = format!(
"Generated {} items ({} immutable, {} mutable)",
stored_data.len(),
immutable_data_count,
mutable_data_count
);
let underline = (0..message.len()).map(|_| "=").collect::<String>();
println!("\n\t{}\n\t{}", message, underline);
for (i, data) in stored_data.into_iter().enumerate() {
let client = auth.client.clone();
match data {
Data::Immutable(data) => {
if !get_only {
put_idata(&client, data.clone(), i).await;
};
let retrieved_data = unwrap!(client.get_idata(*data.address()).await);
println!("Retrieved chunk #{}: {:?}", i, data.name());
assert_eq!(data, retrieved_data);
}
Data::Mutable(data) => {
if !get_only {
unwrap!(put_mdata(&client, data.clone(), i).await);
};
let retrieved_data =
unwrap!(client.get_seq_mdata_shell(*data.name(), data.tag()).await);
assert_eq!(data, retrieved_data);
println!("Retrieved chunk #{}: {:?}", i, data.name());
}
}
}
println!("Done");
}
async fn put_idata(client: &AuthClient, data: IData, i: usize) {
unwrap!(client.put_idata(data.clone()).await);
println!("Put PubImmutableData chunk #{}: {:?}", i, data.name());
let retrieved_data = unwrap!(client.get_idata(*data.address()).await);
assert_eq!(data, retrieved_data);
println!(
"Retrieved PubImmutableData chunk #{}: {:?}",
i,
retrieved_data.name()
);
}
async fn put_mdata(client: &AuthClient, data: SeqMutableData, i: usize) -> Result<(), AppError> {
client.put_seq_mutable_data(data.clone()).await?;
println!("Put MutableData chunk #{}: {:?}", i, data.name());
let retrieved_data = client.get_seq_mdata_shell(*data.name(), data.tag()).await?;
assert_eq!(data, retrieved_data);
println!(
"Retrieved MutableData chunk #{}: {:?}",
i,
retrieved_data.name()
);
Ok(())
}