main.rs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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. //! <https://darkrenaissance.github.io/darkfi/crypto/rln.html>
  19. use std::{collections::HashMap, time::Instant};
  20. use darkfi::{
  21. zk::{empty_witnesses, halo2::Value, Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit},
  22. zkas::ZkBinary,
  23. };
  24. use darkfi_sdk::{
  25. crypto::{pasta_prelude::*, poseidon_hash, MerkleNode, MerkleTree},
  26. pasta::{Ep, Fp},
  27. };
  28. use lazy_static::lazy_static;
  29. use rand::rngs::OsRng;
  30. // These should be unique constants per application.
  31. lazy_static! {
  32. static ref RLN_IDENTIFIER: Fp = Fp::from(42);
  33. static ref IDENTITY_DERIVATION_PATH: Fp = Fp::from(11);
  34. static ref NULLIFIER_DERIVATION_PATH: Fp = Fp::from(12);
  35. }
  36. fn hash_message(message: &[u8]) -> Fp {
  37. let hasher = Ep::hash_to_curve("rln-domain:demoapp");
  38. let message_point = hasher(message);
  39. let message_coords = message_point.to_affine().coordinates().unwrap();
  40. poseidon_hash([*message_coords.x(), *message_coords.y()])
  41. }
  42. fn sss_recover(shares: &[(Fp, Fp)]) -> Fp {
  43. let mut secret = Fp::zero();
  44. for (j, share_j) in shares.iter().enumerate() {
  45. let mut prod = Fp::one();
  46. for (i, share_i) in shares.iter().enumerate() {
  47. if i != j {
  48. prod *= share_i.0 * (share_i.0 - share_j.0).invert().unwrap();
  49. }
  50. }
  51. prod *= share_j.1;
  52. secret += prod;
  53. }
  54. secret
  55. }
  56. fn main() {
  57. let epoch = Fp::from(1674509414);
  58. let external_nullifier = poseidon_hash([epoch, *RLN_IDENTIFIER]);
  59. // The identity commitment should be something that cannot be
  60. // precalculated for usage in the future, and possibly also has
  61. // to be some kind of puzzle that is costly to (pre)calculate.
  62. // Alternatively, it could be economic stake of funds which could
  63. // then be lost if spam is detected and acted upon.
  64. let secret_key = Fp::random(&mut OsRng);
  65. let identity_commitment = poseidon_hash([*IDENTITY_DERIVATION_PATH, secret_key]);
  66. // ============
  67. // Registration
  68. // ============
  69. let mut membership_tree = MerkleTree::new(100);
  70. let mut identity_roots: Vec<MerkleNode> = vec![];
  71. let mut banned_roots: Vec<MerkleNode> = vec![];
  72. let mut identities = HashMap::new();
  73. // Everyone needs to maintain the leaf positions, because to slash, we
  74. // need to provide a valid authentication path. Therefore, the easiest
  75. // way is to store a hashmap.
  76. assert!(!identities.contains_key(&identity_commitment.to_repr()));
  77. membership_tree.append(MerkleNode::from(identity_commitment));
  78. let leaf_pos = membership_tree.mark().unwrap();
  79. identities.insert(identity_commitment.to_repr(), leaf_pos);
  80. identity_roots.push(membership_tree.root(0).unwrap());
  81. // ==========
  82. // Signalling
  83. // ==========
  84. let a_1 = poseidon_hash([secret_key, external_nullifier]);
  85. // Construct share
  86. let x = hash_message(b"hello i wanna spam");
  87. let y = a_1 * x + secret_key;
  88. // Construct internal nullifier
  89. let internal_nullifier = poseidon_hash([*NULLIFIER_DERIVATION_PATH, a_1]);
  90. let identity_root = membership_tree.root(0).unwrap();
  91. let identity_path = membership_tree.witness(leaf_pos, 0).unwrap();
  92. // zkSNARK things
  93. let signal_zkbin = include_bytes!("../signal.zk.bin");
  94. let rln_zkbin = ZkBinary::decode(signal_zkbin).unwrap();
  95. let rln_empty_circuit = ZkCircuit::new(empty_witnesses(&rln_zkbin), rln_zkbin.clone());
  96. print!("[Interaction] Building Proving key... ");
  97. let now = Instant::now();
  98. let rln_pk = ProvingKey::build(13, &rln_empty_circuit);
  99. println!("[{:?}]", now.elapsed());
  100. print!("[Interaction] Building Verifying key... ");
  101. let now = Instant::now();
  102. let rln_vk = VerifyingKey::build(13, &rln_empty_circuit);
  103. println!("[{:?}]", now.elapsed());
  104. // Prover's witnesses and public inputs
  105. let witnesses = vec![
  106. Witness::Base(Value::known(secret_key)),
  107. Witness::MerklePath(Value::known(identity_path.clone().try_into().unwrap())),
  108. Witness::Uint32(Value::known(u64::from(leaf_pos).try_into().unwrap())),
  109. Witness::Base(Value::known(x)),
  110. Witness::Base(Value::known(epoch)),
  111. Witness::Base(Value::known(*RLN_IDENTIFIER)),
  112. ];
  113. let public_inputs = vec![
  114. epoch,
  115. *RLN_IDENTIFIER,
  116. x, // <-- Message hash
  117. identity_root.inner(),
  118. internal_nullifier,
  119. y,
  120. ];
  121. // Build a circuit with these witnesses
  122. print!("[Interaction] Creating ZK proof... ");
  123. let now = Instant::now();
  124. let rln_circuit = ZkCircuit::new(witnesses, rln_zkbin.clone());
  125. let proof = Proof::create(&rln_pk, &[rln_circuit], &public_inputs, &mut OsRng).unwrap();
  126. println!("[{:?}]", now.elapsed());
  127. // ============
  128. // Verification
  129. // ============
  130. print!("[Interaction] Verifying ZK proof... ");
  131. let now = Instant::now();
  132. assert!(proof.verify(&rln_vk, &public_inputs).is_ok());
  133. assert!(!banned_roots.contains(&MerkleNode::from(public_inputs[3])));
  134. assert!(identity_roots.contains(&MerkleNode::from(public_inputs[3])));
  135. println!("[{:?}]", now.elapsed());
  136. // NOTE: These shares should actually be tracked through the internal nullifier.
  137. let mut shares = vec![(public_inputs[2], public_inputs[5])];
  138. // Now if another message is sent in the same epoch, we should be able to
  139. // recover the secret key and ban the sender.
  140. let x = hash_message(b"hello i'm spamming");
  141. let y = a_1 * x + secret_key;
  142. // Same epoch and account, different message
  143. let witnesses = vec![
  144. Witness::Base(Value::known(secret_key)),
  145. Witness::MerklePath(Value::known(identity_path.try_into().unwrap())),
  146. Witness::Uint32(Value::known(u64::from(leaf_pos).try_into().unwrap())),
  147. Witness::Base(Value::known(x)),
  148. Witness::Base(Value::known(epoch)),
  149. Witness::Base(Value::known(*RLN_IDENTIFIER)),
  150. ];
  151. let public_inputs = vec![
  152. epoch,
  153. *RLN_IDENTIFIER,
  154. x, // <-- Message hash
  155. identity_root.inner(),
  156. internal_nullifier,
  157. y,
  158. ];
  159. // Build a circuit with these witnesses
  160. print!("[Interaction] Creating ZK proof... ");
  161. let now = Instant::now();
  162. let rln_circuit = ZkCircuit::new(witnesses, rln_zkbin);
  163. let proof = Proof::create(&rln_pk, &[rln_circuit], &public_inputs, &mut OsRng).unwrap();
  164. println!("[{:?}]", now.elapsed());
  165. print!("[Interaction] Verifying ZK proof... ");
  166. let now = Instant::now();
  167. assert!(proof.verify(&rln_vk, &public_inputs).is_ok());
  168. assert!(!banned_roots.contains(&MerkleNode::from(public_inputs[3])));
  169. assert!(identity_roots.contains(&MerkleNode::from(public_inputs[3])));
  170. println!("[{:?}]", now.elapsed());
  171. // NOTE: These shares should actually be tracked through the internal nullifier.
  172. shares.push((public_inputs[2], public_inputs[5]));
  173. // ========
  174. // Slashing
  175. // ========
  176. // We should be able to retrieve the secret key because two messages were
  177. // sent in the same epoch.
  178. let recovered_secret = sss_recover(&shares);
  179. assert_eq!(recovered_secret, secret_key);
  180. // Create a slash proof
  181. let slash_zkbin = include_bytes!("../slash.zk.bin");
  182. let slash_zkbin = ZkBinary::decode(slash_zkbin).unwrap();
  183. let slash_empty_circuit = ZkCircuit::new(empty_witnesses(&slash_zkbin), slash_zkbin.clone());
  184. print!("[Slash] Building Proving key... ");
  185. let now = Instant::now();
  186. let slash_pk = ProvingKey::build(13, &slash_empty_circuit);
  187. println!("[{:?}]", now.elapsed());
  188. print!("[Slash] Building Verifying key... ");
  189. let now = Instant::now();
  190. let slash_vk = VerifyingKey::build(13, &slash_empty_circuit);
  191. println!("[{:?}]", now.elapsed());
  192. // Find the leaf position in the hashmap of identity commitments
  193. let identity_commitment = poseidon_hash([*IDENTITY_DERIVATION_PATH, recovered_secret]);
  194. let leaf_pos = identities.get(&identity_commitment.to_repr()).unwrap();
  195. let identity_root = membership_tree.root(0).unwrap();
  196. let identity_path = membership_tree.witness(*leaf_pos, 0);
  197. let identity_path = identity_path.unwrap();
  198. // Witnesses & public inputs
  199. let witnesses = vec![
  200. Witness::Base(Value::known(recovered_secret)),
  201. Witness::MerklePath(Value::known(identity_path.try_into().unwrap())),
  202. Witness::Uint32(Value::known(u64::from(*leaf_pos).try_into().unwrap())),
  203. ];
  204. let public_inputs = vec![identity_root.inner()];
  205. print!("[Slash] Creating ZK proof... ");
  206. let now = Instant::now();
  207. let slash_circuit = ZkCircuit::new(witnesses, slash_zkbin);
  208. let proof = Proof::create(&slash_pk, &[slash_circuit], &public_inputs, &mut OsRng).unwrap();
  209. println!("[{:?}]", now.elapsed());
  210. print!("[Slash] Verifying ZK proof... ");
  211. let now = Instant::now();
  212. assert!(!banned_roots.contains(&MerkleNode::from(public_inputs[0])));
  213. assert!(identity_roots.contains(&MerkleNode::from(public_inputs[0]))); // <- Will this be true?
  214. assert!(proof.verify(&slash_vk, &public_inputs).is_ok());
  215. println!("[{:?}]", now.elapsed());
  216. banned_roots.push(MerkleNode::from(public_inputs[0]));
  217. println!("boi u banned");
  218. }