rln.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 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::time::UNIX_EPOCH;
  19. use darkfi::{
  20. event_graph::Event,
  21. zk::{
  22. halo2::{Field, Value},
  23. Proof, ProvingKey, Witness, ZkCircuit,
  24. },
  25. zkas::ZkBinary,
  26. Result,
  27. };
  28. use darkfi_sdk::{
  29. bridgetree::Position,
  30. crypto::{pasta_prelude::FromUniformBytes, poseidon_hash, MerkleTree},
  31. pasta::pallas,
  32. };
  33. use rand::{rngs::OsRng, CryptoRng, RngCore};
  34. use tracing::info;
  35. pub const RLN_APP_IDENTIFIER: pallas::Base = pallas::Base::from_raw([4242, 0, 0, 0]);
  36. pub const RLN_TRAPDOOR_DERIVATION_PATH: pallas::Base = pallas::Base::from_raw([4211, 0, 0, 0]);
  37. pub const RLN_NULLIFIER_DERIVATION_PATH: pallas::Base = pallas::Base::from_raw([4212, 0, 0, 0]);
  38. /// RLN epoch genesis
  39. pub const RLN_GENESIS: u64 = 1738688400;
  40. /// RLN epoch length in seconds
  41. pub const RLN_EPOCH_LEN: u64 = 600; // 10 min
  42. pub const RLN2_SIGNAL_ZKBIN: &[u8] = include_bytes!("../../proof/rlnv2-diff-signal.zk.bin");
  43. pub const RLN2_SLASH_ZKBIN: &[u8] = include_bytes!("../../proof/rlnv2-diff-slash.zk.bin");
  44. /// Find closest epoch to given timestamp
  45. pub fn closest_epoch(timestamp: u64) -> u64 {
  46. let time_diff = timestamp - RLN_GENESIS;
  47. let epoch_idx = time_diff as f64 / RLN_EPOCH_LEN as f64;
  48. let rounded = epoch_idx.round() as i64;
  49. RLN_GENESIS + (rounded * RLN_EPOCH_LEN as i64) as u64
  50. }
  51. /// Hash message/event modulo `Fp`
  52. pub fn hash_event(event: &Event) -> pallas::Base {
  53. let mut buf = [0u8; 64];
  54. buf[..blake3::OUT_LEN].copy_from_slice(event.id().as_bytes());
  55. pallas::Base::from_uniform_bytes(&buf)
  56. }
  57. #[derive(Copy, Clone)]
  58. pub struct RlnIdentity {
  59. pub nullifier: pallas::Base,
  60. pub trapdoor: pallas::Base,
  61. pub user_message_limit: u64,
  62. /// This should increment during a single epoch and reset on new epochs
  63. pub message_id: u64,
  64. /// Last known epoch
  65. pub last_epoch: u64,
  66. }
  67. impl RlnIdentity {
  68. pub fn new(mut rng: (impl CryptoRng + RngCore)) -> Self {
  69. Self {
  70. nullifier: poseidon_hash([
  71. RLN_NULLIFIER_DERIVATION_PATH,
  72. pallas::Base::random(&mut rng),
  73. ]),
  74. trapdoor: poseidon_hash([RLN_TRAPDOOR_DERIVATION_PATH, pallas::Base::random(&mut rng)]),
  75. user_message_limit: 100,
  76. message_id: 1,
  77. last_epoch: closest_epoch(UNIX_EPOCH.elapsed().unwrap().as_secs()),
  78. }
  79. }
  80. pub fn commitment(&self) -> pallas::Base {
  81. poseidon_hash([
  82. poseidon_hash([self.nullifier, self.trapdoor]),
  83. pallas::Base::from(self.user_message_limit),
  84. ])
  85. }
  86. pub fn create_signal_proof(
  87. &self,
  88. event: &Event,
  89. identity_tree: &MerkleTree,
  90. identity_pos: Position,
  91. proving_key: &ProvingKey,
  92. ) -> Result<(Proof, Vec<pallas::Base>)> {
  93. // 1. Construct share
  94. let epoch = pallas::Base::from(closest_epoch(event.timestamp));
  95. let message_id = pallas::Base::from(self.message_id);
  96. let external_nullifier = poseidon_hash([epoch, RLN_APP_IDENTIFIER]);
  97. let a_0 = poseidon_hash([self.nullifier, self.trapdoor]);
  98. let a_1 = poseidon_hash([a_0, external_nullifier, message_id]);
  99. let x = hash_event(event);
  100. let y = a_0 + x * a_1;
  101. let internal_nullifier = poseidon_hash([a_1]);
  102. // 2. Create Merkle proof
  103. let identity_root = identity_tree.root(0).unwrap();
  104. let identity_path = identity_tree.witness(identity_pos, 0).unwrap();
  105. // 3. Create ZK proof
  106. let witnesses = vec![
  107. Witness::Base(Value::known(self.nullifier)),
  108. Witness::Base(Value::known(self.trapdoor)),
  109. Witness::MerklePath(Value::known(identity_path.clone().try_into().unwrap())),
  110. Witness::Uint32(Value::known(u64::from(identity_pos).try_into().unwrap())),
  111. Witness::Base(Value::known(x)),
  112. Witness::Base(Value::known(external_nullifier)),
  113. Witness::Base(Value::known(message_id)),
  114. Witness::Base(Value::known(pallas::Base::from(self.user_message_limit))),
  115. Witness::Base(Value::known(epoch)),
  116. ];
  117. let public_inputs =
  118. vec![epoch, external_nullifier, x, y, internal_nullifier, identity_root.inner()];
  119. info!(target: "crypto::rln::create_proof", "[RLN] Creating proof for event {}", event.id());
  120. let signal_zkbin = ZkBinary::decode(RLN2_SIGNAL_ZKBIN)?;
  121. let signal_circuit = ZkCircuit::new(witnesses, &signal_zkbin);
  122. let proof = Proof::create(proving_key, &[signal_circuit], &public_inputs, &mut OsRng)?;
  123. Ok((proof, vec![y, internal_nullifier]))
  124. }
  125. }
  126. /// Recover a secret from given secret shares
  127. #[allow(dead_code)]
  128. pub fn sss_recover(shares: &[(pallas::Base, pallas::Base)]) -> pallas::Base {
  129. let mut secret = pallas::Base::zero();
  130. for (j, share_j) in shares.iter().enumerate() {
  131. let mut prod = pallas::Base::one();
  132. for (i, share_i) in shares.iter().enumerate() {
  133. if i != j {
  134. prod *= share_i.0 * (share_i.0 - share_j.0).invert().unwrap();
  135. }
  136. }
  137. prod *= share_j.1;
  138. secret += prod;
  139. }
  140. secret
  141. }