vanityaddr/
main.rs
1use std::{
20 process::{exit, ExitCode},
21 sync::{mpsc::channel, Arc},
22 thread::available_parallelism,
23};
24
25use arg::Args;
26use darkfi::{util::cli::ProgressInc, ANSI_LOGO};
27use darkfi_money_contract::{model::TokenId, MoneyFunction};
28use darkfi_sdk::crypto::{
29 contract_id::MONEY_CONTRACT_ID, poseidon_hash, BaseBlind, ContractId, FuncRef, PublicKey,
30 SecretKey,
31};
32use rand::rngs::OsRng;
33use rayon::iter::ParallelIterator;
34
35const ABOUT: &str =
36 concat!("vanityaddr ", env!("CARGO_PKG_VERSION"), '\n', env!("CARGO_PKG_DESCRIPTION"));
37
38const USAGE: &str = r#"
39Usage: vanityaddr [OPTIONS] <PREFIX> <PREFIX> ...
40
41Arguments:
42 <PREFIX> Prefixes to search
43
44Options:
45 -c Make the search case-sensitive
46 -t Number of threads to use (defaults to number of available CPUs)
47 -A Search for an address
48 -C Search for a Contract ID
49 -T Search for a Token ID
50"#;
51
52fn usage() {
53 print!("{}{}\n{}", ANSI_LOGO, ABOUT, USAGE);
54}
55
56struct DrkAddr {
57 pub public: PublicKey,
58 pub secret: SecretKey,
59}
60
61struct DrkToken {
62 pub token_id: TokenId,
63 pub secret: SecretKey,
64 pub blind: BaseBlind,
65}
66
67struct DrkContract {
68 pub contract_id: ContractId,
69 pub secret: SecretKey,
70}
71
72trait Prefixable {
73 fn new() -> Self;
74 fn to_string(&self) -> String;
75 fn _get_secret(&self) -> SecretKey;
76
77 fn starts_with(&self, prefix: &str, case_sensitive: bool) -> bool {
78 if case_sensitive {
79 self.to_string().starts_with(prefix)
80 } else {
81 self.to_string().to_lowercase().starts_with(prefix.to_lowercase().as_str())
82 }
83 }
84
85 fn starts_with_any(&self, prefixes: &[String], case_sensitive: bool) -> bool {
86 prefixes.iter().any(|prefix| self.starts_with(prefix, case_sensitive))
87 }
88}
89
90impl Prefixable for DrkAddr {
91 fn new() -> Self {
92 let secret = SecretKey::random(&mut OsRng);
93 let public = PublicKey::from_secret(secret);
94 Self { public, secret }
95 }
96
97 fn to_string(&self) -> String {
98 self.public.to_string()
99 }
100
101 fn _get_secret(&self) -> SecretKey {
102 self.secret
103 }
104}
105
106impl Prefixable for DrkToken {
107 fn new() -> Self {
108 let secret = SecretKey::random(&mut OsRng);
110 let blind = BaseBlind::random(&mut OsRng);
111
112 let func_id = FuncRef {
114 contract_id: *MONEY_CONTRACT_ID,
115 func_code: MoneyFunction::AuthTokenMintV1 as u8,
116 }
117 .to_func_id();
118
119 let (auth_x, auth_y) = PublicKey::from_secret(secret).xy();
121 let user_data = poseidon_hash([auth_x, auth_y]);
122
123 let token_id = TokenId::derive_from(func_id.inner(), user_data, blind.inner());
125
126 Self { token_id, secret, blind }
127 }
128
129 fn to_string(&self) -> String {
130 self.token_id.to_string()
131 }
132
133 fn _get_secret(&self) -> SecretKey {
134 self.secret
135 }
136}
137
138impl Prefixable for DrkContract {
139 fn new() -> Self {
140 let secret = SecretKey::random(&mut OsRng);
141 let contract_id = ContractId::derive(secret);
142 Self { contract_id, secret }
143 }
144
145 fn to_string(&self) -> String {
146 self.contract_id.to_string()
147 }
148
149 fn _get_secret(&self) -> SecretKey {
150 self.secret
151 }
152}
153
154fn main() -> ExitCode {
155 let argv;
156 let mut hflag = false;
157 let mut cflag = false;
158 let mut addrflag = false;
159 let mut toknflag = false;
160 let mut ctrcflag = false;
161
162 let mut n_threads = available_parallelism().unwrap().get();
163
164 {
165 let mut args = Args::new().with_cb(|args, flag| match flag {
166 'c' => cflag = true,
167 'A' => addrflag = true,
168 'T' => toknflag = true,
169 'C' => ctrcflag = true,
170 't' => n_threads = args.eargf().parse::<usize>().unwrap(),
171 _ => hflag = true,
172 });
173
174 argv = args.parse();
175 }
176
177 if hflag || argv.is_empty() {
178 usage();
179 return ExitCode::FAILURE
180 }
181
182 if (addrflag as u8 + toknflag as u8 + ctrcflag as u8) != 1 {
183 eprintln!("The search flags are mutually exclusive. Use only one of -A/-C/-T.");
184 return ExitCode::FAILURE
185 }
186
187 for (idx, prefix) in argv.iter().enumerate() {
189 match bs58::decode(prefix).into_vec() {
190 Ok(_) => {}
191 Err(e) => {
192 eprintln!("Error: Invalid base58 for prefix #{}: {}", idx, e);
193 return ExitCode::FAILURE
194 }
195 }
196 }
197
198 let (tx, rx) = channel();
200 ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel"))
201 .expect("Error setting SIGINT handler");
202
203 let progress = Arc::new(ProgressInc::new());
205
206 let progress_ = progress.clone();
208 let rayon_pool = rayon::ThreadPoolBuilder::new().num_threads(n_threads).build().unwrap();
209 rayon_pool.spawn(move || {
210 if addrflag {
211 let addr = rayon::iter::repeat(DrkAddr::new)
212 .inspect(|_| progress_.inc(1))
213 .map(|create| create())
214 .find_any(|address| address.starts_with_any(&argv, cflag))
215 .expect("Failed to find an address match");
216
217 let attempts = progress_.position();
221 progress_.finish_and_clear();
222
223 println!(
224 "{{\"address\":\"{}\",\"attempts\":{},\"secret\":\"{}\"}}",
225 addr.public, attempts, addr.secret,
226 );
227 }
228
229 if toknflag {
230 let tid = rayon::iter::repeat(DrkToken::new)
231 .inspect(|_| progress_.inc(1))
232 .map(|create| create())
233 .find_any(|token_id| token_id.starts_with_any(&argv, cflag))
234 .expect("Failed to find a token ID match");
235
236 let attempts = progress_.position();
237 progress_.finish_and_clear();
238
239 println!(
240 "{{\"token_id\":\"{}\",\"attempts\":{},\"secret\":\"{}\",\"blind\":\"{}\"}}",
241 tid.token_id, attempts, tid.secret, tid.blind
242 );
243 }
244
245 if ctrcflag {
246 let cid = rayon::iter::repeat(DrkContract::new)
247 .inspect(|_| progress_.inc(1))
248 .map(|create| create())
249 .find_any(|contract_id| contract_id.starts_with_any(&argv, cflag))
250 .expect("Failed to find a contract ID match");
251
252 let attempts = progress_.position();
253 progress_.finish_and_clear();
254
255 println!(
256 "{{\"contract_id\":\"{}\",\"attempts\":{},\"secret\":\"{}\"}}",
257 cid.contract_id, attempts, cid.secret,
258 );
259 }
260
261 exit(0);
262 });
263
264 rx.recv().expect("Could not receive from channel");
266 progress.finish_and_clear();
267 eprintln!("\r\x1b[2KCaught SIGINT, exiting...");
268 ExitCode::FAILURE
269}