use std::{
env,
error::Error,
fmt::Write as FmtWrite,
fs,
io::{stdout, Write},
time::Instant,
};
use anyhow::anyhow;
use multiaddr::Multiaddr;
use rand::rngs::OsRng;
use tari_comms::{
peer_manager::{NodeId, PeerFeatures},
types::CommsPublicKey,
NodeIdentity,
};
use tari_crypto::tari_utilities::{message_format::MessageFormat, ByteArray};
use tokio::{sync::mpsc, task, task::JoinHandle};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let target_hex_prefixes = env::args().skip(1).map(|s| s.to_lowercase()).collect::<Vec<String>>();
for prefix in target_hex_prefixes {
let now = Instant::now();
let (tx, mut rx) = mpsc::channel(1);
println!("Finding {prefix}...");
spawn_identity_miners(16, tx, prefix);
let found = rx.recv().await.unwrap();
rx.close();
while rx.try_recv().is_ok() {}
println!("Found '{}' in {:.2?}", found.node_id(), now.elapsed());
println!("==================================================");
println!("{found}");
write_identity(found);
}
println!("Done");
Ok(())
}
fn write_identity(identity: NodeIdentity) {
let tmp = env::temp_dir().join("vanity_ids");
fs::create_dir_all(&tmp).unwrap();
let path = tmp.join(format!("{}.json", identity.node_id()));
println!("Wrote '{}'", path.to_str().unwrap());
fs::write(path, identity.to_json().unwrap()).unwrap();
}
fn spawn_identity_miners(
num_miners: usize,
tx: mpsc::Sender<NodeIdentity>,
prefix: String,
) -> Vec<JoinHandle<Result<(), anyhow::Error>>> {
(0..num_miners)
.map(|id| {
let tx = tx.clone();
let prefix = prefix.clone();
task::spawn_blocking(move || start_miner(id, prefix, tx))
})
.collect()
}
fn start_miner(id: usize, prefix: String, tx: mpsc::Sender<NodeIdentity>) -> Result<(), anyhow::Error> {
let mut node_id_hex = String::with_capacity(26);
for i in 0u64.. {
let (k, pk) = CommsPublicKey::random_keypair(&mut OsRng);
let node_id = NodeId::from_public_key(&pk);
node_id_hex.clear();
for byte in node_id.as_bytes() {
write!(&mut node_id_hex, "{byte:02x}").expect("Unable to write");
}
if i % 50_000 == 0 {
if i == 0 {
println!("Worker #{id} - started");
} else {
println!("Worker #{id} - {i} iterations");
}
stdout().flush()?;
}
if tx.is_closed() {
break;
}
if node_id_hex[0..prefix.len()] == *prefix {
if tx
.try_send(NodeIdentity::new(
k,
vec![Multiaddr::empty()],
PeerFeatures::COMMUNICATION_NODE,
))
.is_err()
{
eprintln!("Failed to send");
return Err(anyhow!("Failed to send"));
}
break;
}
}
Ok(())
}