main.rs 7.2 KB

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