main.rs 8.7 KB

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