main.rs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 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,
  20. sync::{mpsc::channel, Arc},
  21. };
  22. use clap::Parser;
  23. use darkfi::util::cli::ProgressInc;
  24. use darkfi_sdk::crypto::{ContractId, PublicKey, SecretKey, TokenId};
  25. use rand::rngs::OsRng;
  26. use rayon::prelude::*;
  27. use darkfi::cli_desc;
  28. #[derive(Parser)]
  29. #[clap(name = "vanityaddr", about = cli_desc!(), version)]
  30. #[clap(arg_required_else_help(true))]
  31. struct Args {
  32. /// Prefixes to search
  33. prefix: Vec<String>,
  34. /// Should the search be case-sensitive
  35. #[clap(short)]
  36. case_sensitive: bool,
  37. /// Search for an Address
  38. #[clap(long)]
  39. address: bool,
  40. /// Search for a Token ID
  41. #[clap(long)]
  42. token_id: bool,
  43. /// Search for a Contract ID
  44. #[clap(long)]
  45. contract_id: bool,
  46. /// Number of threads to use (defaults to number of available CPUs)
  47. #[clap(short)]
  48. threads: Option<usize>,
  49. }
  50. struct DrkAddr {
  51. pub public: PublicKey,
  52. pub secret: SecretKey,
  53. }
  54. struct DrkToken {
  55. pub token_id: TokenId,
  56. pub secret: SecretKey,
  57. }
  58. struct DrkContract {
  59. pub contract_id: ContractId,
  60. pub secret: SecretKey,
  61. }
  62. trait Prefixable {
  63. fn new() -> Self;
  64. fn to_string(&self) -> String;
  65. fn get_secret(&self) -> SecretKey;
  66. fn starts_with(&self, prefix: &str, case_sensitive: bool) -> bool {
  67. if case_sensitive {
  68. self.to_string().starts_with(prefix)
  69. } else {
  70. self.to_string().to_lowercase().starts_with(prefix.to_lowercase().as_str())
  71. }
  72. }
  73. fn starts_with_any(&self, prefixes: &[String], case_sensitive: bool) -> bool {
  74. prefixes.iter().any(|prefix| self.starts_with(prefix, case_sensitive))
  75. }
  76. }
  77. impl Prefixable for DrkAddr {
  78. fn new() -> Self {
  79. let secret = SecretKey::random(&mut OsRng);
  80. let public = PublicKey::from_secret(secret);
  81. Self { public, secret }
  82. }
  83. fn to_string(&self) -> String {
  84. self.public.to_string()
  85. }
  86. fn get_secret(&self) -> SecretKey {
  87. self.secret
  88. }
  89. }
  90. impl Prefixable for DrkToken {
  91. fn new() -> Self {
  92. let secret = SecretKey::random(&mut OsRng);
  93. let token_id = TokenId::derive(secret);
  94. Self { token_id, secret }
  95. }
  96. fn to_string(&self) -> String {
  97. self.token_id.to_string()
  98. }
  99. fn get_secret(&self) -> SecretKey {
  100. self.secret
  101. }
  102. }
  103. impl Prefixable for DrkContract {
  104. fn new() -> Self {
  105. let secret = SecretKey::random(&mut OsRng);
  106. let contract_id = ContractId::derive(secret);
  107. Self { contract_id, secret }
  108. }
  109. fn to_string(&self) -> String {
  110. self.contract_id.to_string()
  111. }
  112. fn get_secret(&self) -> SecretKey {
  113. self.secret
  114. }
  115. }
  116. fn main() {
  117. let args = Args::parse();
  118. if !((args.address ^ args.contract_id ^ args.token_id) &&
  119. !(args.address && args.contract_id && args.token_id))
  120. {
  121. eprintln!("Error: Can only search for one of Address/ContractId/TokenId");
  122. exit(1);
  123. }
  124. if args.prefix.is_empty() {
  125. eprintln!("Error: No prefix given to search.");
  126. exit(1);
  127. }
  128. // Check if prefixes are valid base58
  129. for (idx, prefix) in args.prefix.iter().enumerate() {
  130. match bs58::decode(prefix).into_vec() {
  131. Ok(_) => {}
  132. Err(e) => {
  133. eprintln!("Error: Invalid base58 for prefix {}: {}", idx, e);
  134. exit(1);
  135. }
  136. };
  137. }
  138. // Threadpool
  139. let num_threads = if args.threads.is_some() {
  140. args.threads.unwrap()
  141. } else {
  142. std::thread::available_parallelism().unwrap().get()
  143. };
  144. let rayon_pool = rayon::ThreadPoolBuilder::new().num_threads(num_threads).build().unwrap();
  145. // Handle SIGINT
  146. let (tx, rx) = channel();
  147. ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel"))
  148. .expect("Error setting SIGINT handler");
  149. // Something fancy
  150. let progress = Arc::new(ProgressInc::new());
  151. // Fire off the threadpool
  152. let progress_ = progress.clone();
  153. rayon_pool.spawn(move || {
  154. if args.token_id {
  155. let tid = rayon::iter::repeat(DrkToken::new)
  156. .inspect(|_| progress_.inc(1))
  157. .map(|create| create())
  158. .find_any(|token_id| token_id.starts_with_any(&args.prefix, args.case_sensitive))
  159. .expect("Failed to find a token ID match");
  160. // The above will keep running until it finds a match or until the
  161. // program terminates. Only if a match is found shall the following
  162. // code be executed and the program exit successfully:
  163. let attempts = progress_.position();
  164. progress_.finish_and_clear();
  165. println!(
  166. "{{\"token_id\":\"{}\",\"attempts\":{},\"secret\":\"{}\"}}",
  167. tid.token_id, attempts, tid.secret,
  168. );
  169. } else if args.address {
  170. let addr = rayon::iter::repeat(DrkAddr::new)
  171. .inspect(|_| progress_.inc(1))
  172. .map(|create| create())
  173. .find_any(|address| address.starts_with_any(&args.prefix, args.case_sensitive))
  174. .expect("Failed to find an address match");
  175. let attempts = progress_.position();
  176. progress_.finish_and_clear();
  177. println!(
  178. "{{\"address\":\"{}\",\"attempts\":{},\"secret\":\"{}\"}}",
  179. addr.public, attempts, addr.secret,
  180. );
  181. } else if args.contract_id {
  182. let cid = rayon::iter::repeat(DrkContract::new)
  183. .inspect(|_| progress_.inc(1))
  184. .map(|create| create())
  185. .find_any(|contract_id| {
  186. contract_id.starts_with_any(&args.prefix, args.case_sensitive)
  187. })
  188. .expect("Failed to find a contract ID match");
  189. let attempts = progress_.position();
  190. progress_.finish_and_clear();
  191. println!(
  192. "{{\"contract_id\":\"{}\",\"attempts\":{},\"secret\":\"{}\"}}",
  193. cid.contract_id, attempts, cid.secret,
  194. );
  195. }
  196. exit(0);
  197. });
  198. // This now blocks and lets our threadpool execute in the background.
  199. rx.recv().expect("Could not receive from channel");
  200. progress.finish_and_clear();
  201. eprintln!("\r\x1b[2KCaught SIGINT, exiting...");
  202. exit(127);
  203. }