|
@@ -16,53 +16,43 @@
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
-use std::{
|
|
|
|
|
- collections::BTreeMap,
|
|
|
|
|
- time::{Instant, UNIX_EPOCH},
|
|
|
|
|
-};
|
|
|
|
|
|
|
+use std::time::Instant;
|
|
|
|
|
|
|
|
use darkfi::{
|
|
use darkfi::{
|
|
|
zk::{empty_witnesses, halo2::Value, Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit},
|
|
zk::{empty_witnesses, halo2::Value, Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit},
|
|
|
zkas::ZkBinary,
|
|
zkas::ZkBinary,
|
|
|
};
|
|
};
|
|
|
use darkfi_sdk::{
|
|
use darkfi_sdk::{
|
|
|
- bridgetree::Position,
|
|
|
|
|
- crypto::{pasta_prelude::Field, poseidon_hash, MerkleNode, MerkleTree},
|
|
|
|
|
|
|
+ crypto::{
|
|
|
|
|
+ pasta_prelude::Field,
|
|
|
|
|
+ poseidon_hash,
|
|
|
|
|
+ smt::{MemoryStorageFp, PoseidonFp, SmtMemoryFp, EMPTY_NODES_FP},
|
|
|
|
|
+ },
|
|
|
pasta::{group::ff::FromUniformBytes, pallas},
|
|
pasta::{group::ff::FromUniformBytes, pallas},
|
|
|
};
|
|
};
|
|
|
use rand::rngs::OsRng;
|
|
use rand::rngs::OsRng;
|
|
|
|
|
|
|
|
-struct Account {
|
|
|
|
|
|
|
+struct Identity {
|
|
|
identity_nullifier: pallas::Base,
|
|
identity_nullifier: pallas::Base,
|
|
|
identity_trapdoor: pallas::Base,
|
|
identity_trapdoor: pallas::Base,
|
|
|
- identity_leaf_pos: Position,
|
|
|
|
|
user_message_limit: pallas::Base,
|
|
user_message_limit: pallas::Base,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-impl Account {
|
|
|
|
|
- fn register(
|
|
|
|
|
- membership_tree: &mut MerkleTree,
|
|
|
|
|
- membership_map: &mut BTreeMap<pallas::Base, Position>,
|
|
|
|
|
- ) -> 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 user_message_limit = pallas::Base::from(100);
|
|
|
|
|
- let identity_commitment = poseidon_hash([identity_secret_hash, user_message_limit]);
|
|
|
|
|
-
|
|
|
|
|
- membership_tree.append(MerkleNode::from(identity_commitment));
|
|
|
|
|
- let identity_leaf_pos = membership_tree.mark().unwrap();
|
|
|
|
|
- membership_map.insert(identity_commitment, identity_leaf_pos);
|
|
|
|
|
-
|
|
|
|
|
|
|
+impl Identity {
|
|
|
|
|
+ fn new(user_message_limit: pallas::Base) -> Self {
|
|
|
Self {
|
|
Self {
|
|
|
- identity_nullifier,
|
|
|
|
|
- identity_trapdoor,
|
|
|
|
|
- identity_leaf_pos,
|
|
|
|
|
- // message id < user_message_limit
|
|
|
|
|
|
|
+ identity_nullifier: pallas::Base::random(&mut OsRng),
|
|
|
|
|
+ identity_trapdoor: pallas::Base::random(&mut OsRng),
|
|
|
user_message_limit,
|
|
user_message_limit,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ fn commitment(&self) -> pallas::Base {
|
|
|
|
|
+ let identity_secret = poseidon_hash([self.identity_nullifier, self.identity_trapdoor]);
|
|
|
|
|
+ let identity_secret_hash = poseidon_hash([identity_secret, self.user_message_limit]);
|
|
|
|
|
+
|
|
|
|
|
+ poseidon_hash([identity_secret_hash])
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Hash message modulo Fp
|
|
/// Hash message modulo Fp
|
|
@@ -76,86 +66,70 @@ fn hash_message(msg: &str) -> pallas::Base {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
fn main() {
|
|
|
- // There exists a Merkle tree of identity commitments that serves
|
|
|
|
|
- // as the user registry.
|
|
|
|
|
- let mut membership_tree = MerkleTree::new(1);
|
|
|
|
|
- // Since bridgetree is append-only, we'll maintain a BTreeMap of all the
|
|
|
|
|
- // identity commitments and their indexes. Whenever some idenity is banned
|
|
|
|
|
- // we'll zero out that leaf and rebuild the bridgetree from the BTreeMap.
|
|
|
|
|
- let mut membership_map = BTreeMap::new();
|
|
|
|
|
|
|
+ // There exists a Sparse Merkle Tree of identity commitments that
|
|
|
|
|
+ // serves as the user registry. If a leaf is NULL, it should mean
|
|
|
|
|
+ // that the identity is non-existent and thus should not be accepted.
|
|
|
|
|
+ let hasher = PoseidonFp::new();
|
|
|
|
|
+ let store = MemoryStorageFp::new();
|
|
|
|
|
+ let mut identity_tree = SmtMemoryFp::new(store, hasher.clone(), &EMPTY_NODES_FP);
|
|
|
|
|
|
|
|
// Per-app identifier
|
|
// Per-app identifier
|
|
|
let rln_identifier = pallas::Base::from(42);
|
|
let rln_identifier = pallas::Base::from(42);
|
|
|
|
|
|
|
|
- // Current epoch
|
|
|
|
|
- let epoch = pallas::Base::from(UNIX_EPOCH.elapsed().unwrap().as_secs() as u64);
|
|
|
|
|
|
|
+ // Create three accounts
|
|
|
|
|
+ let id0 = Identity::new(pallas::Base::from(5));
|
|
|
|
|
+ let id1 = Identity::new(pallas::Base::from(2));
|
|
|
|
|
+ let id2 = Identity::new(pallas::Base::from(1));
|
|
|
|
|
|
|
|
- // Register account
|
|
|
|
|
- let account0 = Account::register(&mut membership_tree, &mut membership_map);
|
|
|
|
|
-
|
|
|
|
|
- // ==========
|
|
|
|
|
- // Signalling
|
|
|
|
|
- // ==========
|
|
|
|
|
- let signal_zkbin = include_bytes!("../signal.zk.bin");
|
|
|
|
|
- let signal_zkbin = ZkBinary::decode(signal_zkbin, false).unwrap();
|
|
|
|
|
- let signal_empty_circuit =
|
|
|
|
|
- ZkCircuit::new(empty_witnesses(&signal_zkbin).unwrap(), &signal_zkbin);
|
|
|
|
|
|
|
+ // ============
|
|
|
|
|
+ // Registration
|
|
|
|
|
+ // ============
|
|
|
|
|
+ let register_zkbin = include_bytes!("../register.zk.bin");
|
|
|
|
|
+ let register_zkbin = ZkBinary::decode(register_zkbin).unwrap();
|
|
|
|
|
+ let register_empty_circuit =
|
|
|
|
|
+ ZkCircuit::new(empty_witnesses(®ister_zkbin).unwrap(), ®ister_zkbin);
|
|
|
|
|
|
|
|
- print!("[Signal] Building Proving key... ");
|
|
|
|
|
|
|
+ print!("[Register] Building Proving key... ");
|
|
|
let now = Instant::now();
|
|
let now = Instant::now();
|
|
|
- let signal_pk = ProvingKey::build(signal_zkbin.k, &signal_empty_circuit);
|
|
|
|
|
|
|
+ let register_pk = ProvingKey::build(register_zkbin.k, ®ister_empty_circuit);
|
|
|
println!("[{:?}]", now.elapsed());
|
|
println!("[{:?}]", now.elapsed());
|
|
|
|
|
|
|
|
- print!("[Signal] Building Verifying key... ");
|
|
|
|
|
|
|
+ print!("[Register] Building Verifying key... ");
|
|
|
let now = Instant::now();
|
|
let now = Instant::now();
|
|
|
- let signal_vk = VerifyingKey::build(signal_zkbin.k, &signal_empty_circuit);
|
|
|
|
|
|
|
+ let register_vk = VerifyingKey::build(register_zkbin.k, ®ister_empty_circuit);
|
|
|
println!("[{:?}]", now.elapsed());
|
|
println!("[{:?}]", now.elapsed());
|
|
|
|
|
|
|
|
- // =========================
|
|
|
|
|
- // Account 0 sends a message
|
|
|
|
|
- // =========================
|
|
|
|
|
-
|
|
|
|
|
- // 1. Construct share:
|
|
|
|
|
- let message_id = pallas::Base::from(1);
|
|
|
|
|
- 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, message_id]);
|
|
|
|
|
- let x = hash_message("hello i wanna spam");
|
|
|
|
|
- let y = a_0 + x * a_1;
|
|
|
|
|
-
|
|
|
|
|
- let internal_nullifier = poseidon_hash([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(external_nullifier)),
|
|
|
|
|
- Witness::Base(Value::known(message_id)),
|
|
|
|
|
- Witness::Base(Value::known(account0.user_message_limit)),
|
|
|
|
|
- Witness::Base(Value::known(epoch)),
|
|
|
|
|
- ];
|
|
|
|
|
-
|
|
|
|
|
- let public_inputs =
|
|
|
|
|
- vec![epoch, external_nullifier, x, y, internal_nullifier, 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());
|
|
|
|
|
|
|
+ for (i, id) in [id0, id1, id2].iter().enumerate() {
|
|
|
|
|
+ // Create ZK proof
|
|
|
|
|
+ // This 6 message limit is arbitrary and should likely be based on stake.
|
|
|
|
|
+ let witnesses = vec![
|
|
|
|
|
+ Witness::Base(Value::known(id.identity_nullifier)),
|
|
|
|
|
+ Witness::Base(Value::known(id.identity_trapdoor)),
|
|
|
|
|
+ Witness::Base(Value::known(id.user_message_limit)),
|
|
|
|
|
+ Witness::Base(Value::known(pallas::Base::from(6))),
|
|
|
|
|
+ ];
|
|
|
|
|
+ let public_inputs = vec![id.commitment(), pallas::Base::from(6)];
|
|
|
|
|
+
|
|
|
|
|
+ print!("[Register] Creating ZK proof for id{i}... ");
|
|
|
|
|
+ let now = Instant::now();
|
|
|
|
|
+ let register_circuit = ZkCircuit::new(witnesses, ®ister_zkbin);
|
|
|
|
|
+ let proof =
|
|
|
|
|
+ Proof::create(®ister_pk, &[register_circuit], &public_inputs, &mut OsRng).unwrap();
|
|
|
|
|
+ println!("[{:?}]", now.elapsed());
|
|
|
|
|
+
|
|
|
|
|
+ // Verify ZK proof
|
|
|
|
|
+ print!("[Register] Verifying ZK proof for id{i}... ");
|
|
|
|
|
+ assert!(proof.verify(®ister_vk, &public_inputs).is_ok());
|
|
|
|
|
+ println!("[{:?}]", now.elapsed());
|
|
|
|
|
+
|
|
|
|
|
+ let leaf = vec![id.commitment()];
|
|
|
|
|
+ let leaf: Vec<_> = leaf.into_iter().map(|l| (l, l)).collect();
|
|
|
|
|
+ // TODO: Should verify that identity doesn't exist yet before insert.
|
|
|
|
|
+ identity_tree.insert_batch(leaf.clone()).unwrap(); // leaf == pos
|
|
|
|
|
+ assert_eq!(leaf[0].0, id.commitment());
|
|
|
|
|
+ assert_eq!(leaf[0].1, id.commitment());
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // ============
|
|
|
|
|
- // Verification
|
|
|
|
|
- // ============
|
|
|
|
|
- print!("[Signal] Verifying ZK proof... ");
|
|
|
|
|
- let now = Instant::now();
|
|
|
|
|
- assert!(proof.verify(&signal_vk, &public_inputs).is_ok());
|
|
|
|
|
- println!("[{:?}]", now.elapsed());
|
|
|
|
|
|
|
+ // At this point we have 3 identities registered. They also all have
|
|
|
|
|
+ // different message limits per epoch.
|
|
|
}
|
|
}
|