|
|
@@ -0,0 +1,171 @@
|
|
|
+/* This file is part of DarkFi (https://dark.fi)
|
|
|
+ *
|
|
|
+ * Copyright (C) 2020-2023 Dyne.org foundation
|
|
|
+ *
|
|
|
+ * This program is free software: you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU Affero General Public License as
|
|
|
+ * published by the Free Software Foundation, either version 3 of the
|
|
|
+ * License, or (at your option) any later version.
|
|
|
+ *
|
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU Affero General Public License for more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU Affero General Public License
|
|
|
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
+ */
|
|
|
+
|
|
|
+use std::{
|
|
|
+ collections::BTreeMap,
|
|
|
+ time::{Instant, UNIX_EPOCH},
|
|
|
+};
|
|
|
+
|
|
|
+use darkfi::{
|
|
|
+ zk::{empty_witnesses, halo2::Value, Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit},
|
|
|
+ zkas::ZkBinary,
|
|
|
+};
|
|
|
+use darkfi_sdk::{
|
|
|
+ bridgetree::Position,
|
|
|
+ crypto::{pasta_prelude::Field, poseidon_hash, MerkleNode, MerkleTree},
|
|
|
+ pasta::{group::ff::FromUniformBytes, pallas},
|
|
|
+};
|
|
|
+use rand::rngs::OsRng;
|
|
|
+
|
|
|
+struct Account {
|
|
|
+ identity_nullifier: pallas::Base,
|
|
|
+ identity_trapdoor: pallas::Base,
|
|
|
+ identity_leaf_pos: Position,
|
|
|
+ msgid: pallas::Base,
|
|
|
+}
|
|
|
+
|
|
|
+impl Account {
|
|
|
+ fn register(
|
|
|
+ membership_tree: &mut MerkleTree,
|
|
|
+ membership_map: &mut BTreeMap<Position, pallas::Base>,
|
|
|
+ ) -> Self {
|
|
|
+ let identity_nullifier = pallas::Base::random(&mut OsRng);
|
|
|
+ let identity_trapdoor = pallas::Base::random(&mut OsRng);
|
|
|
+
|
|
|
+ let identity_secret_hash = poseidon_hash([identity_nullifier, identity_trapdoor]);
|
|
|
+ let identity_commitment = poseidon_hash([identity_secret_hash]);
|
|
|
+
|
|
|
+ membership_tree.append(MerkleNode::from(identity_commitment));
|
|
|
+ let identity_leaf_pos = membership_tree.mark().unwrap();
|
|
|
+ membership_map.insert(identity_leaf_pos, identity_commitment);
|
|
|
+
|
|
|
+ Self { identity_nullifier, identity_trapdoor, identity_leaf_pos }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// Hash message modulo Fp
|
|
|
+/// In DarkIRC/eventgraph this could be the event ID
|
|
|
+fn hash_message(msg: &str) -> pallas::Base {
|
|
|
+ let message_hash = blake3::hash(msg.as_bytes());
|
|
|
+
|
|
|
+ let mut buf = [0u8; 64];
|
|
|
+ buf[..blake3::OUT_LEN].copy_from_slice(message_hash.as_bytes());
|
|
|
+ pallas::Base::from_uniform_bytes(&buf)
|
|
|
+}
|
|
|
+
|
|
|
+fn main() {
|
|
|
+ // There exists a Merkle tree of identity commitments that serves
|
|
|
+ // as the user registry.
|
|
|
+ let mut membership_tree = MerkleTree::new(100);
|
|
|
+ // Since bridgetree is append-only, we'll maintain a BTreeMap of all the
|
|
|
+ // identity commitments in their indexes and whenever some idenity is banned
|
|
|
+ // we'll zero out that leaf and rebuild the bridgetree from the BTreeMap.
|
|
|
+ let mut membership_map = BTreeMap::new();
|
|
|
+
|
|
|
+ // The global message limit per-account per-epoch
|
|
|
+ let message_limit = pallas::Base::from(3);
|
|
|
+
|
|
|
+ // Per-app identifier
|
|
|
+ let rln_identifier = pallas::Base::from(42);
|
|
|
+
|
|
|
+ // Current epoch
|
|
|
+ let epoch = pallas::Base::from(UNIX_EPOCH.elapsed().unwrap().as_secs() as u64);
|
|
|
+
|
|
|
+ // Register three accounts
|
|
|
+ let account0 = Account::register(&mut membership_tree, &mut membership_map);
|
|
|
+ let account0_msgid = pallas::Base::from(0);
|
|
|
+
|
|
|
+ /*
|
|
|
+ let account1 = Account::register(&mut membership_tree, &mut membership_map);
|
|
|
+ let account1_msgid = pallas::Base::from(0);
|
|
|
+
|
|
|
+ let account2 = Account::register(&mut membership_tree, &mut membership_map);
|
|
|
+ let account2_msgid = pallas::Base::from(0);
|
|
|
+ */
|
|
|
+
|
|
|
+ // ==========
|
|
|
+ // Signalling
|
|
|
+ // ==========
|
|
|
+ let signal_zkbin = include_bytes!("../signal.zk.bin");
|
|
|
+ let signal_zkbin = ZkBinary::decode(signal_zkbin).unwrap();
|
|
|
+ let signal_empty_circuit =
|
|
|
+ ZkCircuit::new(empty_witnesses(&signal_zkbin).unwrap(), &signal_zkbin);
|
|
|
+
|
|
|
+ print!("[Signal] Building Proving key... ");
|
|
|
+ let now = Instant::now();
|
|
|
+ let signal_pk = ProvingKey::build(signal_zkbin.k, &signal_empty_circuit);
|
|
|
+ println!("[{:?}]", now.elapsed());
|
|
|
+
|
|
|
+ print!("[Signal] Building Verifying key... ");
|
|
|
+ let now = Instant::now();
|
|
|
+ let signal_vk = VerifyingKey::build(signal_zkbin.k, &signal_empty_circuit);
|
|
|
+ println!("[{:?}]", now.elapsed());
|
|
|
+
|
|
|
+ // =========================
|
|
|
+ // Account 0 sends a message
|
|
|
+ // =========================
|
|
|
+
|
|
|
+ // 1. Construct share:
|
|
|
+ let external_nullifier = poseidon_hash([epoch, rln_identifier]);
|
|
|
+ let a_0 = poseidon_hash([account0.identity_nullifier, account0.identity_trapdoor]);
|
|
|
+ let a_1 = poseidon_hash([a_0, external_nullifier, account0_msgid]);
|
|
|
+ let internal_nullifier = poseidon_hash([a_1]);
|
|
|
+ let x = hash_message("hello i wanna spam");
|
|
|
+ let y = a_0 + x * a_1;
|
|
|
+
|
|
|
+ // 2. Create Merkle proof:
|
|
|
+ let identity_root = membership_tree.root(0).unwrap();
|
|
|
+ let identity_path = membership_tree.witness(account0.identity_leaf_pos, 0).unwrap();
|
|
|
+
|
|
|
+ // 3. Create ZK proof:
|
|
|
+ let witnesses = vec![
|
|
|
+ Witness::Base(Value::known(account0.identity_nullifier)),
|
|
|
+ Witness::Base(Value::known(account0.identity_trapdoor)),
|
|
|
+ Witness::MerklePath(Value::known(identity_path.clone().try_into().unwrap())),
|
|
|
+ Witness::Uint32(Value::known(u64::from(account0.identity_leaf_pos).try_into().unwrap())),
|
|
|
+ Witness::Base(Value::known(x)),
|
|
|
+ Witness::Base(Value::known(account0_msgid)),
|
|
|
+ Witness::Base(Value::known(message_limit)),
|
|
|
+ Witness::Base(Value::known(epoch)),
|
|
|
+ Witness::Base(Value::known(rln_identifier)),
|
|
|
+ ];
|
|
|
+
|
|
|
+ let public_inputs = vec![
|
|
|
+ message_limit,
|
|
|
+ epoch,
|
|
|
+ external_nullifier,
|
|
|
+ internal_nullifier,
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ identity_root.inner(),
|
|
|
+ ];
|
|
|
+
|
|
|
+ print!("[Signal] Creating ZK proof for 0:0...");
|
|
|
+ let now = Instant::now();
|
|
|
+ let signal_circuit = ZkCircuit::new(witnesses, &signal_zkbin);
|
|
|
+ let proof = Proof::create(&signal_pk, &[signal_circuit], &public_inputs, &mut OsRng).unwrap();
|
|
|
+ println!("[{:?}]", now.elapsed());
|
|
|
+
|
|
|
+ // ============
|
|
|
+ // Verification
|
|
|
+ // ============
|
|
|
+ print!("[Signal] Verifying ZK proof... ");
|
|
|
+ let now = Instant::now();
|
|
|
+ assert!(proof.verify(&signal_vk, &public_inputs).is_ok());
|
|
|
+ println!("[{:?}]", now.elapsed());
|
|
|
+}
|