main.rs
1 /* This file is part of DarkFi (https://dark.fi) 2 * 3 * Copyright (C) 2020-2025 Dyne.org foundation 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation, either version 3 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Affero General Public License for more details. 14 * 15 * You should have received a copy of the GNU Affero General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19 use std::{ 20 process::{exit, ExitCode}, 21 sync::{mpsc::channel, Arc}, 22 thread::available_parallelism, 23 }; 24 25 use arg::Args; 26 use darkfi::{util::cli::ProgressInc, ANSI_LOGO}; 27 use darkfi_money_contract::{model::TokenId, MoneyFunction}; 28 use darkfi_sdk::crypto::{ 29 contract_id::MONEY_CONTRACT_ID, poseidon_hash, BaseBlind, ContractId, FuncRef, PublicKey, 30 SecretKey, 31 }; 32 use rand::rngs::OsRng; 33 use rayon::iter::ParallelIterator; 34 35 const ABOUT: &str = 36 concat!("vanityaddr ", env!("CARGO_PKG_VERSION"), '\n', env!("CARGO_PKG_DESCRIPTION")); 37 38 const USAGE: &str = r#" 39 Usage: vanityaddr [OPTIONS] <PREFIX> <PREFIX> ... 40 41 Arguments: 42 <PREFIX> Prefixes to search 43 44 Options: 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 52 fn usage() { 53 print!("{ANSI_LOGO}{ABOUT}\n{USAGE}"); 54 } 55 56 struct DrkAddr { 57 pub public: PublicKey, 58 pub secret: SecretKey, 59 } 60 61 struct DrkToken { 62 pub token_id: TokenId, 63 pub secret: SecretKey, 64 pub blind: BaseBlind, 65 } 66 67 struct DrkContract { 68 pub contract_id: ContractId, 69 pub secret: SecretKey, 70 } 71 72 trait 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 90 impl 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 106 impl Prefixable for DrkToken { 107 fn new() -> Self { 108 // Generate the mint authority secret key and blind 109 let secret = SecretKey::random(&mut OsRng); 110 let blind = BaseBlind::random(&mut OsRng); 111 112 // Create the Auth FuncID 113 let func_id = FuncRef { 114 contract_id: *MONEY_CONTRACT_ID, 115 func_code: MoneyFunction::AuthTokenMintV1 as u8, 116 } 117 .to_func_id(); 118 119 // Grab the mint authority user data 120 let (auth_x, auth_y) = PublicKey::from_secret(secret).xy(); 121 let user_data = poseidon_hash([auth_x, auth_y]); 122 123 // Derive the Token ID 124 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 138 impl 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 154 fn 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 // Validate search prefixes 188 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 // Handle SIGINT 199 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 // Something fancy 204 let progress = Arc::new(ProgressInc::new()); 205 206 // Threadpool 207 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 // The above will keep running until it finds a match or until 218 // the program terminates. Only if a match is found shall the 219 // following code be executed and the program exit successfully: 220 let attempts = progress_.position(); 221 progress_.finish_and_clear(); 222 223 println!( 224 "{{\"address\":\"{}\",\"attempts\":{attempts},\"secret\":\"{}\"}}", 225 addr.public, 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\":{attempts},\"secret\":\"{}\",\"blind\":\"{}\"}}", 241 tid.token_id, 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}\",\"attempts\":{},\"secret\":\"{}\"}}", 257 cid.contract_id, cid.secret, 258 ); 259 } 260 261 exit(0); 262 }); 263 264 // This now blocks and lets our threadpool execute in the background. 265 rx.recv().expect("Could not receive from channel"); 266 progress.finish_and_clear(); 267 eprintln!("\r\x1b[2KCaught SIGINT, exiting..."); 268 ExitCode::FAILURE 269 }