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}