main.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. collections::BTreeMap,
  20. time::{Instant, UNIX_EPOCH},
  21. };
  22. use darkfi::{
  23. zk::{empty_witnesses, halo2::Value, Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit},
  24. zkas::ZkBinary,
  25. };
  26. use darkfi_sdk::{
  27. crypto::{
  28. pasta_prelude::Field,
  29. poseidon_hash,
  30. smt::{MemoryStorageFp, PoseidonFp, SmtMemoryFp, EMPTY_NODES_FP},
  31. },
  32. pasta::{group::ff::FromUniformBytes, pallas},
  33. };
  34. use rand::rngs::OsRng;
  35. #[derive(Copy, Clone)]
  36. struct Identity {
  37. identity_nullifier: pallas::Base,
  38. identity_trapdoor: pallas::Base,
  39. user_message_limit: pallas::Base,
  40. }
  41. impl Identity {
  42. fn new(user_message_limit: pallas::Base) -> Self {
  43. Self {
  44. identity_nullifier: pallas::Base::random(&mut OsRng),
  45. identity_trapdoor: pallas::Base::random(&mut OsRng),
  46. user_message_limit,
  47. }
  48. }
  49. fn commitment(&self) -> pallas::Base {
  50. let identity_secret = poseidon_hash([self.identity_nullifier, self.identity_trapdoor]);
  51. let identity_secret_hash = poseidon_hash([identity_secret, self.user_message_limit]);
  52. poseidon_hash([identity_secret_hash])
  53. }
  54. }
  55. #[derive(Debug, Clone)]
  56. struct ShareData {
  57. pub x_shares: Vec<pallas::Base>,
  58. pub y_shares: Vec<pallas::Base>,
  59. }
  60. impl ShareData {
  61. fn new() -> Self {
  62. Self { x_shares: vec![], y_shares: vec![] }
  63. }
  64. }
  65. #[derive(Debug, Default)]
  66. struct MessageMetadata {
  67. data: BTreeMap<pallas::Base, BTreeMap<pallas::Base, ShareData>>,
  68. }
  69. impl MessageMetadata {
  70. fn new() -> Self {
  71. Self { data: BTreeMap::new() }
  72. }
  73. fn add_share(
  74. &mut self,
  75. external_nullifier: pallas::Base,
  76. internal_nullifier: pallas::Base,
  77. x: pallas::Base,
  78. y: pallas::Base,
  79. ) {
  80. let inner_map = self.data.entry(external_nullifier).or_insert_with(BTreeMap::new);
  81. let share_data = inner_map.entry(internal_nullifier).or_insert_with(ShareData::new);
  82. share_data.x_shares.push(x);
  83. share_data.y_shares.push(y);
  84. }
  85. fn get_shares(
  86. &self,
  87. external_nullifier: &pallas::Base,
  88. internal_nullifier: &pallas::Base,
  89. ) -> Vec<(pallas::Base, pallas::Base)> {
  90. if let Some(inner_map) = self.data.get(external_nullifier) {
  91. if let Some(share_data) = inner_map.get(internal_nullifier) {
  92. return share_data
  93. .x_shares
  94. .iter()
  95. .cloned()
  96. .zip(share_data.y_shares.iter().cloned())
  97. .collect()
  98. }
  99. }
  100. vec![]
  101. }
  102. fn is_duplicate(
  103. &self,
  104. external_nullifier: &pallas::Base,
  105. internal_nullifier: &pallas::Base,
  106. x: &pallas::Base,
  107. y: &pallas::Base,
  108. ) -> bool {
  109. if let Some(inner_map) = self.data.get(external_nullifier) {
  110. if let Some(share_data) = inner_map.get(internal_nullifier) {
  111. return share_data.x_shares.contains(x) && share_data.y_shares.contains(y);
  112. }
  113. }
  114. false
  115. }
  116. }
  117. /// Hash message modulo Fp
  118. /// In DarkIRC/eventgraph this could be the event ID
  119. fn hash_message(msg: &str) -> pallas::Base {
  120. let message_hash = blake3::hash(msg.as_bytes());
  121. let mut buf = [0u8; 64];
  122. buf[..blake3::OUT_LEN].copy_from_slice(message_hash.as_bytes());
  123. pallas::Base::from_uniform_bytes(&buf)
  124. }
  125. fn sss_recover(shares: &[(pallas::Base, pallas::Base)]) -> pallas::Base {
  126. let mut secret = pallas::Base::zero();
  127. for (j, share_j) in shares.iter().enumerate() {
  128. let mut prod = pallas::Base::one();
  129. for (i, share_i) in shares.iter().enumerate() {
  130. if i != j {
  131. prod *= share_i.0 * (share_i.0 - share_j.0).invert().unwrap();
  132. }
  133. }
  134. prod *= share_j.1;
  135. secret += prod;
  136. }
  137. secret
  138. }
  139. fn main() {
  140. // There exists a Sparse Merkle Tree of identity commitments that
  141. // serves as the user registry. If a leaf is NULL, it should mean
  142. // that the identity is non-existent and thus should not be accepted.
  143. let hasher = PoseidonFp::new();
  144. let store = MemoryStorageFp::new();
  145. let mut identity_tree = SmtMemoryFp::new(store, hasher.clone(), &EMPTY_NODES_FP);
  146. // Per-app identifier
  147. let rln_identifier = pallas::Base::from(1000);
  148. // Create two accounts
  149. let id0 = Identity::new(pallas::Base::from(2));
  150. let id1 = Identity::new(pallas::Base::from(1));
  151. // ============
  152. // Registration
  153. // ============
  154. let register_zkbin = include_bytes!("../register.zk.bin");
  155. let register_zkbin = ZkBinary::decode(register_zkbin).unwrap();
  156. let register_empty_circuit =
  157. ZkCircuit::new(empty_witnesses(&register_zkbin).unwrap(), &register_zkbin);
  158. print!("[Register] Building Proving key... ");
  159. let now = Instant::now();
  160. let register_pk = ProvingKey::build(register_zkbin.k, &register_empty_circuit);
  161. println!("[{:?}]", now.elapsed());
  162. print!("[Register] Building Verifying key... ");
  163. let now = Instant::now();
  164. let register_vk = VerifyingKey::build(register_zkbin.k, &register_empty_circuit);
  165. println!("[{:?}]", now.elapsed());
  166. for (i, id) in [id0, id1].iter().enumerate() {
  167. // Create ZK proof
  168. // This 6 message limit is arbitrary and should likely be based on stake.
  169. let witnesses = vec![
  170. Witness::Base(Value::known(id.identity_nullifier)),
  171. Witness::Base(Value::known(id.identity_trapdoor)),
  172. Witness::Base(Value::known(id.user_message_limit)),
  173. Witness::Base(Value::known(pallas::Base::from(6))),
  174. ];
  175. let public_inputs = vec![id.commitment(), pallas::Base::from(6)];
  176. print!("[Register] Creating ZK proof for id{i}... ");
  177. let now = Instant::now();
  178. let register_circuit = ZkCircuit::new(witnesses, &register_zkbin);
  179. let proof =
  180. Proof::create(&register_pk, &[register_circuit], &public_inputs, &mut OsRng).unwrap();
  181. println!("[{:?}]", now.elapsed());
  182. // Verify ZK proof
  183. print!("[Register] Verifying ZK proof for id{i}... ");
  184. let now = Instant::now();
  185. assert!(proof.verify(&register_vk, &public_inputs).is_ok());
  186. println!("[{:?}]", now.elapsed());
  187. let leaf = vec![id.commitment()];
  188. let leaf: Vec<_> = leaf.into_iter().map(|l| (l, l)).collect();
  189. // TODO: Recipients should verify that identity doesn't exist already before insert.
  190. identity_tree.insert_batch(leaf.clone()).unwrap(); // leaf == pos
  191. assert_eq!(leaf[0].0, id.commitment());
  192. assert_eq!(leaf[0].1, id.commitment());
  193. }
  194. // At this point we have 2 identities registered.
  195. // ==========
  196. // Signalling
  197. // ==========
  198. let signal_zkbin = include_bytes!("../signal.zk.bin");
  199. let signal_zkbin = ZkBinary::decode(signal_zkbin).unwrap();
  200. let signal_empty_circuit =
  201. ZkCircuit::new(empty_witnesses(&signal_zkbin).unwrap(), &signal_zkbin);
  202. print!("[Signal] Building Proving key... ");
  203. let now = Instant::now();
  204. let signal_pk = ProvingKey::build(signal_zkbin.k, &signal_empty_circuit);
  205. println!("[{:?}]", now.elapsed());
  206. print!("[Signal] Building Verifying key... ");
  207. let now = Instant::now();
  208. let signal_vk = VerifyingKey::build(signal_zkbin.k, &signal_empty_circuit);
  209. println!("[{:?}]", now.elapsed());
  210. // Our epoch length will be 10s. This normally means one message every
  211. // 10 seconds. In RLNv2-DIFF we have N messages every epoch.
  212. // This works because of integer division, so for example:
  213. // 1697472000, 1697472005, and 1697472009 are the same epoch, but
  214. // 1697472010 would be the new epoch.
  215. // In practice, if our client realizes we're sending too fast, we could
  216. // also queue it.
  217. let epoch_len = 10_u64;
  218. // =========================
  219. // Account 0 sends a message
  220. // =========================
  221. // 1. Construct share
  222. let epoch = pallas::Base::from(UNIX_EPOCH.elapsed().unwrap().as_secs() as u64 / epoch_len);
  223. let message_id = pallas::Base::from(0); // This should increment each msg
  224. let external_nullifier = poseidon_hash([epoch, rln_identifier]);
  225. let a_0 = poseidon_hash([id0.identity_nullifier, id0.identity_trapdoor]);
  226. let a_1 = poseidon_hash([a_0, external_nullifier, message_id]);
  227. let x = hash_message("hello");
  228. let y = a_0 + x * a_1;
  229. let internal_nullifier = poseidon_hash([a_1]);
  230. // 2. Inclusion proof
  231. let root = identity_tree.root();
  232. let path = identity_tree.prove_membership(&id0.commitment());
  233. assert!(path.verify(&root, &id0.commitment(), &id0.commitment()));
  234. // 3. ZK proof
  235. let witnesses = vec![
  236. Witness::Base(Value::known(id0.identity_nullifier)),
  237. Witness::Base(Value::known(id0.identity_trapdoor)),
  238. Witness::Base(Value::known(id0.user_message_limit)),
  239. Witness::SparseMerklePath(Value::known(path.path)),
  240. Witness::Base(Value::known(x)),
  241. Witness::Base(Value::known(message_id)),
  242. Witness::Base(Value::known(epoch)),
  243. ];
  244. let public_inputs = vec![root, external_nullifier, x, y, internal_nullifier];
  245. print!("[Signal] Creating ZK proof for 0:0... ");
  246. let now = Instant::now();
  247. let signal_circuit = ZkCircuit::new(witnesses, &signal_zkbin);
  248. let proof = Proof::create(&signal_pk, &[signal_circuit], &public_inputs, &mut OsRng).unwrap();
  249. print!("[{:?}] ", now.elapsed());
  250. println!("({} bytes)", proof.as_ref().len());
  251. // ============
  252. // Verification
  253. // ============
  254. print!("[Signal] Verifying ZK proof for 0:0... ");
  255. let now = Instant::now();
  256. assert!(proof.verify(&signal_vk, &public_inputs).is_ok());
  257. println!("[{:?}]", now.elapsed());
  258. // Each user of the protocol must store metadata for each message
  259. // received by each user, for the given epoch. The data can be
  260. // deleted when the epoch passes.
  261. let mut metadata = MessageMetadata::new();
  262. if metadata.is_duplicate(&external_nullifier, &internal_nullifier, &x, &y) {
  263. println!("[Signal] Duplicate Message!");
  264. return
  265. }
  266. // Add share
  267. metadata.add_share(external_nullifier, internal_nullifier, x, y);
  268. // Now let's try to send another message in the same epoch.
  269. // id0 has a limit of 2 so it should pass since the ZK circuit will
  270. // allow this.
  271. let message_id = message_id + pallas::Base::from(1);
  272. let a_0 = poseidon_hash([id0.identity_nullifier, id0.identity_trapdoor]);
  273. let a_1 = poseidon_hash([a_0, external_nullifier, message_id]);
  274. let x = hash_message("hello again");
  275. let y = a_0 + x * a_1;
  276. let internal_nullifier = poseidon_hash([a_1]);
  277. // Skip the inclusion proof for the demo since we have it above.
  278. // Make the ZK proof.
  279. let witnesses = vec![
  280. Witness::Base(Value::known(id0.identity_nullifier)),
  281. Witness::Base(Value::known(id0.identity_trapdoor)),
  282. Witness::Base(Value::known(id0.user_message_limit)),
  283. Witness::SparseMerklePath(Value::known(path.path)),
  284. Witness::Base(Value::known(x)),
  285. Witness::Base(Value::known(message_id)),
  286. Witness::Base(Value::known(epoch)),
  287. ];
  288. let public_inputs = vec![root, external_nullifier, x, y, internal_nullifier];
  289. print!("[Signal] Creating ZK proof for 0:1... ");
  290. let now = Instant::now();
  291. let signal_circuit = ZkCircuit::new(witnesses, &signal_zkbin);
  292. let proof = Proof::create(&signal_pk, &[signal_circuit], &public_inputs, &mut OsRng).unwrap();
  293. print!("[{:?}] ", now.elapsed());
  294. println!("({} bytes)", proof.as_ref().len());
  295. print!("[Signal] Verifying ZK proof for 0:1... ");
  296. let now = Instant::now();
  297. assert!(proof.verify(&signal_vk, &public_inputs).is_ok());
  298. println!("[{:?}]", now.elapsed());
  299. // Each user of the protocol must store metadata for each message
  300. // received by each user, for the given epoch. The data can be
  301. // deleted when the epoch passes.
  302. if metadata.is_duplicate(&external_nullifier, &internal_nullifier, &x, &y) {
  303. println!("[Signal] Duplicate Message!");
  304. return
  305. }
  306. // Add share
  307. metadata.add_share(external_nullifier, internal_nullifier, x, y);
  308. // Now we shouldn't be able to create more proofs unless we reuse message_id.
  309. // This means that some internal_nullifier will have >1 shares, and it should
  310. // be possible to recover the secret.
  311. // We reuse the above, just try a different message.
  312. let x = hash_message("hello again, i'm reusing a message_id");
  313. let y = a_0 + x * a_1;
  314. // ZK proof:
  315. let witnesses = vec![
  316. Witness::Base(Value::known(id0.identity_nullifier)),
  317. Witness::Base(Value::known(id0.identity_trapdoor)),
  318. Witness::Base(Value::known(id0.user_message_limit)),
  319. Witness::SparseMerklePath(Value::known(path.path)),
  320. Witness::Base(Value::known(x)),
  321. Witness::Base(Value::known(message_id)),
  322. Witness::Base(Value::known(epoch)),
  323. ];
  324. let public_inputs = vec![root, external_nullifier, x, y, internal_nullifier];
  325. print!("[Signal] Creating ZK proof for 0:1 (reused message_id) ... ");
  326. let now = Instant::now();
  327. let signal_circuit = ZkCircuit::new(witnesses, &signal_zkbin);
  328. let proof = Proof::create(&signal_pk, &[signal_circuit], &public_inputs, &mut OsRng).unwrap();
  329. print!("[{:?}] ", now.elapsed());
  330. println!("({} bytes)", proof.as_ref().len());
  331. print!("[Signal] Verifying ZK proof for 0:1 (reused message_id) ... ");
  332. let now = Instant::now();
  333. assert!(proof.verify(&signal_vk, &public_inputs).is_ok());
  334. println!("[{:?}]", now.elapsed());
  335. // Add share
  336. metadata.add_share(external_nullifier, internal_nullifier, x, y);
  337. // Now the internal_nullifier should have been repeated, and the internal
  338. // nullifier should have 2 (or more) shares.
  339. // Let's recover them.
  340. let shares = metadata.get_shares(&external_nullifier, &internal_nullifier);
  341. println!("{:#?}", shares);
  342. let secret = sss_recover(&shares);
  343. println!("secret: {:?}", secret);
  344. println!("a_0: {:?}", a_0);
  345. assert_eq!(secret, a_0);
  346. // Additionally, it should not be possible to produce (or verify) a ZK
  347. // proof that exceeds the set message limit for an identity.
  348. let message_id = message_id + pallas::Base::from(2);
  349. let a_0 = poseidon_hash([id0.identity_nullifier, id0.identity_trapdoor]);
  350. let a_1 = poseidon_hash([a_0, external_nullifier, message_id]);
  351. let x = hash_message("hello again");
  352. let y = a_0 + x * a_1;
  353. let internal_nullifier = poseidon_hash([a_1]);
  354. // Skip the inclusion proof for the demo since we have it above.
  355. // Make the ZK proof.
  356. let witnesses = vec![
  357. Witness::Base(Value::known(id0.identity_nullifier)),
  358. Witness::Base(Value::known(id0.identity_trapdoor)),
  359. Witness::Base(Value::known(id0.user_message_limit)),
  360. Witness::SparseMerklePath(Value::known(path.path)),
  361. Witness::Base(Value::known(x)),
  362. Witness::Base(Value::known(message_id)),
  363. Witness::Base(Value::known(epoch)),
  364. ];
  365. let public_inputs = vec![root, external_nullifier, x, y, internal_nullifier];
  366. println!("[Signal] Creating ZK proof for 0:2 msgid={:?}... ", message_id);
  367. let signal_circuit = ZkCircuit::new(witnesses, &signal_zkbin);
  368. let proof = Proof::create(&signal_pk, &[signal_circuit], &public_inputs, &mut OsRng).unwrap();
  369. assert!(proof.verify(&signal_vk, &public_inputs).is_err());
  370. println!("[Signal] ZK proof for 0:2 failed as expected");
  371. }