use crate::vanity;
use crate::vanity::Secret;
use clap::Clap;
use std::sync::{Arc, RwLock};
use tokio::time::{timeout, Duration, Instant};
#[derive(Clap)]
pub struct VanityOpts {
#[clap(subcommand)]
secret_type: VanitySecretType,
}
impl VanityOpts {
pub async fn handle(&self) -> anyhow::Result<()> {
let (secret_type, opts) = match &self.secret_type {
VanitySecretType::Phrase(phrase) => {
let secret_type = vanity::SecretType::Phrase {
language: phrase.phrase_opts.language.language.to_owned(),
words: phrase.phrase_opts.words.0,
};
(secret_type, &phrase.common_opts)
}
VanitySecretType::Seed(seed) => (vanity::SecretType::Seed, &seed.common_opts),
VanitySecretType::Private(private) => {
(vanity::SecretType::Private, &private.common_opts)
}
};
let matches = if opts.start {
vanity::Match::start(&opts.matching)
} else if opts.end {
vanity::Match::end(&opts.matching)
} else if opts.regex {
vanity::Match::regex(&opts.matching)?
} else {
vanity::Match::start_or_end(&opts.matching)
};
let mut vanity = vanity::Vanity::new(secret_type, matches);
if let Some(tasks) = opts.tasks {
vanity.tasks(tasks);
}
if opts.include_digit {
vanity.include_first_digit(true);
}
let (mut rx, attempts) = vanity.start().await?;
let started = Instant::now();
let mut last_log = Instant::now();
let mut found = 0;
loop {
match timeout(Duration::from_secs(1), rx.recv()).await {
Ok(Some(result)) => {
let s = match result.secret {
Secret::Phrase(p) => p.to_string(),
Secret::Seed(s) => s.to_string(),
Secret::Private(p) => p.to_string(),
};
println!("{},{}", result.address.to_string(), s);
last_log = log(started, last_log, attempts.clone()).await;
found += 1;
if let Some(limit) = opts.limit {
if limit == found {
break;
}
}
}
Ok(None) => {
break;
}
Err(_) => {
last_log = log(started, last_log, attempts.clone()).await;
}
}
}
Ok(())
}
}
async fn log(started: Instant, last_log: Instant, attempts: Arc<RwLock<usize>>) -> Instant {
let now = Instant::now();
let since_last_log = now.duration_since(last_log);
if since_last_log < Duration::from_secs(1) {
last_log
} else {
let total_taken = Instant::now().duration_since(started);
let attempts = *attempts
.read()
.expect("Could not unlock attempts for reading.");
let rate = (attempts as f64) / total_taken.as_secs_f64();
eprintln!("Attempted: {}, Rate: {:?} attempts/s", attempts, rate);
now
}
}
#[derive(Clap)]
enum VanitySecretType {
Phrase(PhraseOpts),
Seed(SeedOpts),
Private(PrivateOpts),
}
#[derive(Clap)]
struct PhraseOpts {
#[clap(flatten)]
pub phrase_opts: super::phrase::New,
#[clap(flatten)]
pub common_opts: CommonOpts,
}
#[derive(Clap)]
struct SeedOpts {
#[clap(flatten)]
pub common_opts: CommonOpts,
}
#[derive(Clap)]
struct PrivateOpts {
#[clap(flatten)]
pub common_opts: CommonOpts,
}
#[derive(Clap)]
struct CommonOpts {
matching: String,
#[clap(short, long, group = "match")]
start: bool,
#[clap(short, long, group = "match")]
end: bool,
#[clap(short, long, group = "match")]
regex: bool,
#[clap(short, long)]
include_digit: bool,
#[clap(short, long)]
tasks: Option<usize>,
#[clap(short, long)]
limit: Option<usize>,
}