vanikey/
lib.rs

1use bech32::{ToBase32, Variant};
2use secp256k1::{KeyPair, Secp256k1, XOnlyPublicKey};
3use std::sync::{
4    atomic::{AtomicBool, Ordering},
5    mpsc::{self},
6    Arc,
7};
8use std::thread;
9
10pub struct NostrKeyGenerator {
11    thread_count: u32,
12}
13
14impl NostrKeyGenerator {
15    pub fn new(thread_count: u32) -> Self {
16        Self { thread_count }
17    }
18
19    pub fn thread_count(&self) -> u32 {
20        self.thread_count
21    }
22
23    pub fn generate_key() -> KeyPair {
24        let secp = Secp256k1::new();
25        let mut rng = rand::rngs::OsRng::default();
26        KeyPair::new(&secp, &mut rng)
27    }
28
29    pub fn to_npub(pubkey: &XOnlyPublicKey) -> String {
30        bech32::encode("npub", pubkey.serialize().to_base32(), Variant::Bech32).unwrap()
31    }
32
33    pub fn to_nsec(keypair: &KeyPair) -> String {
34        bech32::encode(
35            "nsec",
36            keypair.secret_key().secret_bytes().to_base32(),
37            Variant::Bech32,
38        )
39        .unwrap()
40    }
41
42    pub fn find_vanity_key(
43        &self,
44        primary_prefix: &str,
45        additional_prefixes: Option<&[String]>,
46    ) -> (String, String) {
47        let (sender, receiver) = mpsc::channel();
48        let found = Arc::new(AtomicBool::new(false));
49
50        let mut handles = vec![];
51        for _ in 0..self.thread_count {
52            let sender = sender.clone();
53            let found = found.clone();
54            let primary_prefix = primary_prefix.to_string();
55            let thread_additional = additional_prefixes
56                .map(|prefixes| prefixes.iter().map(|s| s.clone()).collect::<Vec<String>>());
57
58            let handle = thread::spawn(move || {
59                while !found.load(Ordering::Relaxed) {
60                    let keypair = Self::generate_key();
61                    let (pubkey, _) = keypair.x_only_public_key();
62
63                    let npub = Self::to_npub(&pubkey);
64                    let nsec = Self::to_nsec(&keypair);
65
66                    if let Some(ref additional) = thread_additional {
67                        for add_prefix in additional {
68                            if npub.starts_with(&format!("npub1{}", add_prefix)) {
69                                println!("\nFound additional match!");
70                                println!("Public Key (npub): {}", npub);
71                                println!("Private Key (nsec): {}", nsec);
72                                println!("\n Continuing search for primary prefix...");
73                            }
74                        }
75                    }
76
77                    if npub.starts_with(&format!("npub1{}", primary_prefix)) {
78                        let (derived_pubkey, _) = keypair.x_only_public_key();
79                        assert_eq!(pubkey, derived_pubkey, "Key pair mismatch!");
80
81                        found.store(true, Ordering::Relaxed);
82                        let _ = sender.send((npub, nsec));
83                        break;
84                    }
85                }
86            });
87            handles.push(handle);
88        }
89        let result = receiver.recv().unwrap();
90        for handle in handles {
91            let _ = handle.join();
92        }
93        result
94    }
95}