main.rs 4.5 KB

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