main.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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::{process::exit, sync::mpsc::channel};
  19. use clap::Parser;
  20. use darkfi_sdk::crypto::{PublicKey, SecretKey, TokenId};
  21. use indicatif::{ProgressBar, ProgressStyle};
  22. use rand::rngs::OsRng;
  23. use rayon::prelude::*;
  24. use darkfi::cli_desc;
  25. #[derive(Parser)]
  26. #[clap(name = "vanityaddr", about = cli_desc!(), version)]
  27. #[clap(arg_required_else_help(true))]
  28. struct Args {
  29. /// Prefixes to search
  30. prefix: Vec<String>,
  31. /// Should the search be case-sensitive
  32. #[clap(short)]
  33. case_sensitive: bool,
  34. /// Search for TokenId instead of an address
  35. #[clap(long)]
  36. token_id: bool,
  37. /// Number of threads to use (defaults to number of available CPUs)
  38. #[clap(short)]
  39. threads: Option<usize>,
  40. }
  41. struct DrkAddr {
  42. pub public: PublicKey,
  43. pub secret: SecretKey,
  44. }
  45. impl DrkAddr {
  46. pub fn new() -> Self {
  47. let secret = SecretKey::random(&mut OsRng);
  48. let public = PublicKey::from_secret(secret);
  49. Self { public, secret }
  50. }
  51. pub fn starts_with(&self, prefix: &str, case_sensitive: bool) -> bool {
  52. if case_sensitive {
  53. self.public.to_string().starts_with(prefix)
  54. } else {
  55. self.public.to_string().to_lowercase().starts_with(prefix.to_lowercase().as_str())
  56. }
  57. }
  58. pub fn starts_with_any(&self, prefixes: &[String], case_sensitive: bool) -> bool {
  59. for prefix in prefixes {
  60. if self.starts_with(prefix, case_sensitive) {
  61. return true
  62. }
  63. }
  64. false
  65. }
  66. }
  67. struct DrkToken {
  68. pub token_id: TokenId,
  69. pub secret: SecretKey,
  70. }
  71. impl DrkToken {
  72. pub fn new() -> Self {
  73. let secret = SecretKey::random(&mut OsRng);
  74. let token_id = TokenId::derive(secret);
  75. Self { token_id, secret }
  76. }
  77. pub fn starts_with(&self, prefix: &str, case_sensitive: bool) -> bool {
  78. if case_sensitive {
  79. self.token_id.to_string().starts_with(prefix)
  80. } else {
  81. self.token_id.to_string().to_lowercase().starts_with(prefix.to_lowercase().as_str())
  82. }
  83. }
  84. pub fn starts_with_any(&self, prefixes: &[String], case_sensitive: bool) -> bool {
  85. for prefix in prefixes {
  86. if self.starts_with(prefix, case_sensitive) {
  87. return true
  88. }
  89. }
  90. false
  91. }
  92. }
  93. fn main() {
  94. let args = Args::parse();
  95. if args.prefix.is_empty() {
  96. eprintln!("Error: No prefix given to search.");
  97. exit(1);
  98. }
  99. // Check if prefixes are valid base58
  100. for (idx, prefix) in args.prefix.iter().enumerate() {
  101. match bs58::decode(prefix).into_vec() {
  102. Ok(_) => {}
  103. Err(e) => {
  104. eprintln!("Error: Invalid base58 for prefix {}: {}", idx, e);
  105. exit(1);
  106. }
  107. };
  108. }
  109. // Threadpool
  110. let num_threads = if args.threads.is_some() { args.threads.unwrap() } else { num_cpus::get() };
  111. let rayon_pool = rayon::ThreadPoolBuilder::new().num_threads(num_threads).build().unwrap();
  112. // Handle SIGINT
  113. let (tx, rx) = channel();
  114. ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel"))
  115. .expect("Error setting SIGINT handler");
  116. // Something fancy
  117. let progress = ProgressBar::new_spinner();
  118. let template =
  119. ProgressStyle::default_bar().template("[{elapsed_precise}] {pos} attempts").unwrap();
  120. progress.set_style(template);
  121. // Fire off the threadpool
  122. rayon_pool.spawn(move || {
  123. if args.token_id {
  124. let tid = rayon::iter::repeat(DrkToken::new)
  125. .inspect(|_| progress.inc(1))
  126. .map(|create| create())
  127. .find_any(|token_id| token_id.starts_with_any(&args.prefix, args.case_sensitive))
  128. .expect("Failed to find a token ID match");
  129. // The above will keep running until it finds a match or until the
  130. // program terminates. Only if a match is found shall the following
  131. // code be executed and the program exit successfully:
  132. let attempts = progress.position();
  133. progress.finish_and_clear();
  134. println!(
  135. "{{\"token_id\":\"{}\",\"attempts\":{},\"secret\":\"{}\"}}",
  136. tid.token_id, attempts, tid.secret,
  137. );
  138. } else {
  139. let addr = rayon::iter::repeat(DrkAddr::new)
  140. .inspect(|_| progress.inc(1))
  141. .map(|create| create())
  142. .find_any(|address| address.starts_with_any(&args.prefix, args.case_sensitive))
  143. .expect("Failed to find an address match");
  144. let attempts = progress.position();
  145. progress.finish_and_clear();
  146. println!(
  147. "{{\"address\":\"{}\",\"attempts\":{},\"secret\":\"{}\"}}",
  148. addr.public, attempts, addr.secret,
  149. );
  150. }
  151. exit(0);
  152. });
  153. // This now blocks and lets our threadpool execute in the background.
  154. rx.recv().expect("Could not receive from channel");
  155. eprintln!("\rCaught SIGINT, exiting...");
  156. exit(127);
  157. }