/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2023 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::{ process::{exit, ExitCode}, sync::{mpsc::channel, Arc}, thread::available_parallelism, }; use arg::Args; use darkfi::{util::cli::ProgressInc, ANSI_LOGO}; use darkfi_sdk::crypto::{ContractId, PublicKey, SecretKey, TokenId}; use rand::rngs::OsRng; use rayon::iter::ParallelIterator; const ABOUT: &str = concat!("vanityaddr ", env!("CARGO_PKG_VERSION"), '\n', env!("CARGO_PKG_DESCRIPTION")); const USAGE: &str = r#" Usage: vanityaddr [OPTIONS] ... Arguments: Prefixes to search Options: -c Make the search case-sensitive -t Number of threads to use (defaults to number of available CPUs) -A Search for an address -C Search for a Contract ID -T Search for a Token ID "#; fn usage() { print!("{}{}\n{}", ANSI_LOGO, ABOUT, USAGE); } struct DrkAddr { pub public: PublicKey, pub secret: SecretKey, } struct DrkToken { pub token_id: TokenId, pub secret: SecretKey, } struct DrkContract { pub contract_id: ContractId, pub secret: SecretKey, } trait Prefixable { fn new() -> Self; fn to_string(&self) -> String; fn get_secret(&self) -> SecretKey; fn starts_with(&self, prefix: &str, case_sensitive: bool) -> bool { if case_sensitive { self.to_string().starts_with(prefix) } else { self.to_string().to_lowercase().starts_with(prefix.to_lowercase().as_str()) } } fn starts_with_any(&self, prefixes: &[String], case_sensitive: bool) -> bool { prefixes.iter().any(|prefix| self.starts_with(prefix, case_sensitive)) } } impl Prefixable for DrkAddr { fn new() -> Self { let secret = SecretKey::random(&mut OsRng); let public = PublicKey::from_secret(secret); Self { public, secret } } fn to_string(&self) -> String { self.public.to_string() } fn get_secret(&self) -> SecretKey { self.secret } } impl Prefixable for DrkToken { fn new() -> Self { let secret = SecretKey::random(&mut OsRng); let token_id = TokenId::derive(secret); Self { token_id, secret } } fn to_string(&self) -> String { self.token_id.to_string() } fn get_secret(&self) -> SecretKey { self.secret } } impl Prefixable for DrkContract { fn new() -> Self { let secret = SecretKey::random(&mut OsRng); let contract_id = ContractId::derive(secret); Self { contract_id, secret } } fn to_string(&self) -> String { self.contract_id.to_string() } fn get_secret(&self) -> SecretKey { self.secret } } fn main() -> ExitCode { let argv; let mut hflag = false; let mut cflag = false; let mut addrflag = false; let mut toknflag = false; let mut ctrcflag = false; let mut n_threads = available_parallelism().unwrap().get(); { let mut args = Args::new().with_cb(|args, flag| match flag { 'c' => cflag = true, 'A' => addrflag = true, 'T' => toknflag = true, 'C' => ctrcflag = true, 't' => n_threads = args.eargf().parse::().unwrap(), _ => hflag = true, }); argv = args.parse(); } if hflag || argv.is_empty() { usage(); return ExitCode::FAILURE } if (addrflag as u8 + toknflag as u8 + ctrcflag as u8) != 1 { eprintln!("The search flags are mutually exclusive. Use only one of -A/-C/-T."); return ExitCode::FAILURE } // Validate search prefixes for (idx, prefix) in argv.iter().enumerate() { match bs58::decode(prefix).into_vec() { Ok(_) => {} Err(e) => { eprintln!("Error: Invalid base58 for prefix #{}: {}", idx, e); return ExitCode::FAILURE } } } // Handle SIGINT let (tx, rx) = channel(); ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel")) .expect("Error setting SIGINT handler"); // Something fancy let progress = Arc::new(ProgressInc::new()); // Threadpool let progress_ = progress.clone(); let rayon_pool = rayon::ThreadPoolBuilder::new().num_threads(n_threads).build().unwrap(); rayon_pool.spawn(move || { if addrflag { let addr = rayon::iter::repeat(DrkAddr::new) .inspect(|_| progress_.inc(1)) .map(|create| create()) .find_any(|address| address.starts_with_any(&argv, cflag)) .expect("Failed to find an address match"); // The above will keep running until it finds a match or until // the program terminates. Only if a match is found shall the // following code be executed and the program exit successfully: let attempts = progress_.position(); progress_.finish_and_clear(); println!( "{{\"address\":\"{}\",\"attempts\":{},\"secret\":\"{}\"}}", addr.public, attempts, addr.secret, ); } if toknflag { let tid = rayon::iter::repeat(DrkToken::new) .inspect(|_| progress_.inc(1)) .map(|create| create()) .find_any(|token_id| token_id.starts_with_any(&argv, cflag)) .expect("Failed to find a token ID match"); let attempts = progress_.position(); progress_.finish_and_clear(); println!( "{{\"token_id\":\"{}\",\"attempts\":{},\"secret\":\"{}\"}}", tid.token_id, attempts, tid.secret, ); } if ctrcflag { let cid = rayon::iter::repeat(DrkContract::new) .inspect(|_| progress_.inc(1)) .map(|create| create()) .find_any(|contract_id| contract_id.starts_with_any(&argv, cflag)) .expect("Failed to find a contract ID match"); let attempts = progress_.position(); progress_.finish_and_clear(); println!( "{{\"contract_id\":\"{}\",\"attempts\":{},\"secret\":\"{}\"}}", cid.contract_id, attempts, cid.secret, ); } exit(0); }); // This now blocks and lets our threadpool execute in the background. rx.recv().expect("Could not receive from channel"); progress.finish_and_clear(); eprintln!("\r\x1b[2KCaught SIGINT, exiting..."); ExitCode::FAILURE }