main.rs 7.9 KB

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