main.rs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  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::{collections::HashMap, env, fs, process};
  19. use darkfi_serial::{deserialize, serialize};
  20. const COMMITMENTS_PATH: &str = "darkirc_rln_commits.bin";
  21. const GENESIS_USER_MSG_LIMIT: u64 = 100;
  22. type Commitment = [u8; 32];
  23. type Secret = [u8; 32];
  24. type IdentityRecord = (Secret, Secret, bool);
  25. type Commitments = HashMap<Commitment, IdentityRecord>;
  26. fn main() {
  27. if let Err(err) = run() {
  28. eprintln!("error: {err}");
  29. process::exit(1);
  30. }
  31. }
  32. /// Dispatch the requested command.
  33. fn run() -> Result<(), String> {
  34. let mut args = env::args().skip(1);
  35. let Some(command) = args.next() else {
  36. usage();
  37. return Err("missing command".to_string());
  38. };
  39. if args.next().is_some() {
  40. usage();
  41. return Err("too many arguments".to_string());
  42. }
  43. match command.as_str() {
  44. "reset" => reset(),
  45. "produce" => produce(),
  46. "help" | "-h" | "--help" => {
  47. usage();
  48. Ok(())
  49. }
  50. _ => {
  51. usage();
  52. Err(format!("unknown command `{command}`"))
  53. }
  54. }
  55. }
  56. /// Reset every generated identity so it can be produced again.
  57. fn reset() -> Result<(), String> {
  58. let mut commitments = load_commitments()?;
  59. for (_, _, used) in commitments.values_mut() {
  60. *used = false;
  61. }
  62. let count = commitments.len();
  63. write_commitments(&commitments)?;
  64. println!("reset {count} commitments");
  65. Ok(())
  66. }
  67. /// Mark the first available identity as used and print its registration command.
  68. fn produce() -> Result<(), String> {
  69. let mut commitments = load_commitments()?;
  70. let mut available = commitments
  71. .iter()
  72. .filter_map(|(commitment, (_, _, used))| (!*used).then_some(*commitment))
  73. .collect::<Vec<_>>();
  74. available.sort_unstable();
  75. let Some(commitment) = available.into_iter().next() else {
  76. return Err("no unused commitments available".to_string());
  77. };
  78. let (nullifier, trapdoor) = {
  79. let record = commitments
  80. .get_mut(&commitment)
  81. .ok_or_else(|| "selected commitment disappeared".to_string())?;
  82. record.2 = true;
  83. (record.0, record.1)
  84. };
  85. write_commitments(&commitments)?;
  86. print_credentials(nullifier, trapdoor);
  87. Ok(())
  88. }
  89. /// Load the commitments map from the DarkIRC genesis identity file.
  90. fn load_commitments() -> Result<Commitments, String> {
  91. let bytes = fs::read(COMMITMENTS_PATH)
  92. .map_err(|err| format!("failed to read `{COMMITMENTS_PATH}`: {err}"))?;
  93. deserialize(&bytes).map_err(|err| format!("failed to deserialize `{COMMITMENTS_PATH}`: {err}"))
  94. }
  95. /// Write the commitments map back to the DarkIRC genesis identity file.
  96. fn write_commitments(commitments: &Commitments) -> Result<(), String> {
  97. fs::write(COMMITMENTS_PATH, serialize(commitments))
  98. .map_err(|err| format!("failed to write `{COMMITMENTS_PATH}`: {err}"))
  99. }
  100. /// Print the fields needed by DarkIRC NickServ registration.
  101. fn print_credentials(nullifier: Secret, trapdoor: Secret) {
  102. let nullifier = bs58::encode(nullifier).into_string();
  103. let trapdoor = bs58::encode(trapdoor).into_string();
  104. println!("nullifier = {nullifier}");
  105. println!("trapdoor = {trapdoor}");
  106. println!("user_msg_limit = {GENESIS_USER_MSG_LIMIT}");
  107. println!();
  108. println!(
  109. "/msg NickServ REGISTER <account_name> {nullifier} {trapdoor} {GENESIS_USER_MSG_LIMIT}"
  110. );
  111. }
  112. /// Print command usage.
  113. fn usage() {
  114. println!("Usage: rln-fau <reset|produce>");
  115. }