|
@@ -16,7 +16,10 @@
|
|
|
* 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::time::Instant;
|
|
|
|
|
|
|
+use std::{
|
|
|
|
|
+ collections::BTreeMap,
|
|
|
|
|
+ time::{Instant, UNIX_EPOCH},
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
use darkfi::{
|
|
use darkfi::{
|
|
|
zk::{empty_witnesses, halo2::Value, Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit},
|
|
zk::{empty_witnesses, halo2::Value, Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit},
|
|
@@ -32,6 +35,7 @@ use darkfi_sdk::{
|
|
|
};
|
|
};
|
|
|
use rand::rngs::OsRng;
|
|
use rand::rngs::OsRng;
|
|
|
|
|
|
|
|
|
|
+#[derive(Copy, Clone)]
|
|
|
struct Identity {
|
|
struct Identity {
|
|
|
identity_nullifier: pallas::Base,
|
|
identity_nullifier: pallas::Base,
|
|
|
identity_trapdoor: pallas::Base,
|
|
identity_trapdoor: pallas::Base,
|
|
@@ -55,6 +59,78 @@ impl Identity {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#[derive(Debug, Clone)]
|
|
|
|
|
+struct ShareData {
|
|
|
|
|
+ pub x_shares: Vec<pallas::Base>,
|
|
|
|
|
+ pub y_shares: Vec<pallas::Base>,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl ShareData {
|
|
|
|
|
+ fn new() -> Self {
|
|
|
|
|
+ Self { x_shares: vec![], y_shares: vec![] }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Debug, Default)]
|
|
|
|
|
+struct MessageMetadata {
|
|
|
|
|
+ data: BTreeMap<pallas::Base, BTreeMap<pallas::Base, ShareData>>,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl MessageMetadata {
|
|
|
|
|
+ fn new() -> Self {
|
|
|
|
|
+ Self { data: BTreeMap::new() }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn add_share(
|
|
|
|
|
+ &mut self,
|
|
|
|
|
+ external_nullifier: pallas::Base,
|
|
|
|
|
+ internal_nullifier: pallas::Base,
|
|
|
|
|
+ x: pallas::Base,
|
|
|
|
|
+ y: pallas::Base,
|
|
|
|
|
+ ) {
|
|
|
|
|
+ let inner_map = self.data.entry(external_nullifier).or_insert_with(BTreeMap::new);
|
|
|
|
|
+ let share_data = inner_map.entry(internal_nullifier).or_insert_with(ShareData::new);
|
|
|
|
|
+
|
|
|
|
|
+ share_data.x_shares.push(x);
|
|
|
|
|
+ share_data.y_shares.push(y);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn get_shares(
|
|
|
|
|
+ &self,
|
|
|
|
|
+ external_nullifier: &pallas::Base,
|
|
|
|
|
+ internal_nullifier: &pallas::Base,
|
|
|
|
|
+ ) -> Vec<(pallas::Base, pallas::Base)> {
|
|
|
|
|
+ if let Some(inner_map) = self.data.get(external_nullifier) {
|
|
|
|
|
+ if let Some(share_data) = inner_map.get(internal_nullifier) {
|
|
|
|
|
+ return share_data
|
|
|
|
|
+ .x_shares
|
|
|
|
|
+ .iter()
|
|
|
|
|
+ .cloned()
|
|
|
|
|
+ .zip(share_data.y_shares.iter().cloned())
|
|
|
|
|
+ .collect()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ vec![]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn is_duplicate(
|
|
|
|
|
+ &self,
|
|
|
|
|
+ external_nullifier: &pallas::Base,
|
|
|
|
|
+ internal_nullifier: &pallas::Base,
|
|
|
|
|
+ x: &pallas::Base,
|
|
|
|
|
+ y: &pallas::Base,
|
|
|
|
|
+ ) -> bool {
|
|
|
|
|
+ if let Some(inner_map) = self.data.get(external_nullifier) {
|
|
|
|
|
+ if let Some(share_data) = inner_map.get(internal_nullifier) {
|
|
|
|
|
+ return share_data.x_shares.contains(x) && share_data.y_shares.contains(y);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/// Hash message modulo Fp
|
|
/// Hash message modulo Fp
|
|
|
/// In DarkIRC/eventgraph this could be the event ID
|
|
/// In DarkIRC/eventgraph this could be the event ID
|
|
|
fn hash_message(msg: &str) -> pallas::Base {
|
|
fn hash_message(msg: &str) -> pallas::Base {
|
|
@@ -65,6 +141,23 @@ fn hash_message(msg: &str) -> pallas::Base {
|
|
|
pallas::Base::from_uniform_bytes(&buf)
|
|
pallas::Base::from_uniform_bytes(&buf)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+fn sss_recover(shares: &[(pallas::Base, pallas::Base)]) -> pallas::Base {
|
|
|
|
|
+ let mut secret = pallas::Base::zero();
|
|
|
|
|
+ for (j, share_j) in shares.iter().enumerate() {
|
|
|
|
|
+ let mut prod = pallas::Base::one();
|
|
|
|
|
+ for (i, share_i) in shares.iter().enumerate() {
|
|
|
|
|
+ if i != j {
|
|
|
|
|
+ prod *= share_i.0 * (share_i.0 - share_j.0).invert().unwrap();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ prod *= share_j.1;
|
|
|
|
|
+ secret += prod;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ secret
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
fn main() {
|
|
fn main() {
|
|
|
// There exists a Sparse Merkle Tree of identity commitments that
|
|
// There exists a Sparse Merkle Tree of identity commitments that
|
|
|
// serves as the user registry. If a leaf is NULL, it should mean
|
|
// serves as the user registry. If a leaf is NULL, it should mean
|
|
@@ -74,12 +167,11 @@ fn main() {
|
|
|
let mut identity_tree = SmtMemoryFp::new(store, hasher.clone(), &EMPTY_NODES_FP);
|
|
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(1000);
|
|
|
|
|
|
|
|
- // 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));
|
|
|
|
|
|
|
+ // Create two accounts
|
|
|
|
|
+ let id0 = Identity::new(pallas::Base::from(2));
|
|
|
|
|
+ let id1 = Identity::new(pallas::Base::from(1));
|
|
|
|
|
|
|
|
// ============
|
|
// ============
|
|
|
// Registration
|
|
// Registration
|
|
@@ -99,7 +191,7 @@ fn main() {
|
|
|
let register_vk = VerifyingKey::build(register_zkbin.k, ®ister_empty_circuit);
|
|
let register_vk = VerifyingKey::build(register_zkbin.k, ®ister_empty_circuit);
|
|
|
println!("[{:?}]", now.elapsed());
|
|
println!("[{:?}]", now.elapsed());
|
|
|
|
|
|
|
|
- for (i, id) in [id0, id1, id2].iter().enumerate() {
|
|
|
|
|
|
|
+ for (i, id) in [id0, id1].iter().enumerate() {
|
|
|
// Create ZK proof
|
|
// Create ZK proof
|
|
|
// This 6 message limit is arbitrary and should likely be based on stake.
|
|
// This 6 message limit is arbitrary and should likely be based on stake.
|
|
|
let witnesses = vec![
|
|
let witnesses = vec![
|
|
@@ -119,17 +211,227 @@ fn main() {
|
|
|
|
|
|
|
|
// Verify ZK proof
|
|
// Verify ZK proof
|
|
|
print!("[Register] Verifying ZK proof for id{i}... ");
|
|
print!("[Register] Verifying ZK proof for id{i}... ");
|
|
|
|
|
+ let now = Instant::now();
|
|
|
assert!(proof.verify(®ister_vk, &public_inputs).is_ok());
|
|
assert!(proof.verify(®ister_vk, &public_inputs).is_ok());
|
|
|
println!("[{:?}]", now.elapsed());
|
|
println!("[{:?}]", now.elapsed());
|
|
|
|
|
|
|
|
let leaf = vec![id.commitment()];
|
|
let leaf = vec![id.commitment()];
|
|
|
let leaf: Vec<_> = leaf.into_iter().map(|l| (l, l)).collect();
|
|
let leaf: Vec<_> = leaf.into_iter().map(|l| (l, l)).collect();
|
|
|
- // TODO: Should verify that identity doesn't exist yet before insert.
|
|
|
|
|
|
|
+ // TODO: Recipients should verify that identity doesn't exist already before insert.
|
|
|
identity_tree.insert_batch(leaf.clone()).unwrap(); // leaf == pos
|
|
identity_tree.insert_batch(leaf.clone()).unwrap(); // leaf == pos
|
|
|
assert_eq!(leaf[0].0, id.commitment());
|
|
assert_eq!(leaf[0].0, id.commitment());
|
|
|
assert_eq!(leaf[0].1, id.commitment());
|
|
assert_eq!(leaf[0].1, id.commitment());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // At this point we have 3 identities registered. They also all have
|
|
|
|
|
- // different message limits per epoch.
|
|
|
|
|
|
|
+ // At this point we have 2 identities registered.
|
|
|
|
|
+
|
|
|
|
|
+ // ==========
|
|
|
|
|
+ // 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());
|
|
|
|
|
+
|
|
|
|
|
+ // Our epoch length will be 10s. This normally means one message every
|
|
|
|
|
+ // 10 seconds. In RLNv2-DIFF we have N messages every epoch.
|
|
|
|
|
+ // This works because of integer division, so for example:
|
|
|
|
|
+ // 1697472000, 1697472005, and 1697472009 are the same epoch, but
|
|
|
|
|
+ // 1697472010 would be the new epoch.
|
|
|
|
|
+ // In practice, if our client realizes we're sending too fast, we could
|
|
|
|
|
+ // also queue it.
|
|
|
|
|
+ let epoch_len = 10_u64;
|
|
|
|
|
+
|
|
|
|
|
+ // =========================
|
|
|
|
|
+ // Account 0 sends a message
|
|
|
|
|
+ // =========================
|
|
|
|
|
+
|
|
|
|
|
+ // 1. Construct share
|
|
|
|
|
+ let epoch = pallas::Base::from(UNIX_EPOCH.elapsed().unwrap().as_secs() as u64 / epoch_len);
|
|
|
|
|
+ let message_id = pallas::Base::from(0); // This should increment each msg
|
|
|
|
|
+
|
|
|
|
|
+ let external_nullifier = poseidon_hash([epoch, rln_identifier]);
|
|
|
|
|
+ let a_0 = poseidon_hash([id0.identity_nullifier, id0.identity_trapdoor]);
|
|
|
|
|
+ let a_1 = poseidon_hash([a_0, external_nullifier, message_id]);
|
|
|
|
|
+ let x = hash_message("hello");
|
|
|
|
|
+ let y = a_0 + x * a_1;
|
|
|
|
|
+
|
|
|
|
|
+ let internal_nullifier = poseidon_hash([a_1]);
|
|
|
|
|
+
|
|
|
|
|
+ // 2. Inclusion proof
|
|
|
|
|
+ let root = identity_tree.root();
|
|
|
|
|
+ let path = identity_tree.prove_membership(&id0.commitment());
|
|
|
|
|
+ assert!(path.verify(&root, &id0.commitment(), &id0.commitment()));
|
|
|
|
|
+
|
|
|
|
|
+ // 3. ZK proof
|
|
|
|
|
+ let witnesses = vec![
|
|
|
|
|
+ Witness::Base(Value::known(id0.identity_nullifier)),
|
|
|
|
|
+ Witness::Base(Value::known(id0.identity_trapdoor)),
|
|
|
|
|
+ Witness::Base(Value::known(id0.user_message_limit)),
|
|
|
|
|
+ Witness::SparseMerklePath(Value::known(path.path)),
|
|
|
|
|
+ Witness::Base(Value::known(x)),
|
|
|
|
|
+ Witness::Base(Value::known(message_id)),
|
|
|
|
|
+ Witness::Base(Value::known(epoch)),
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ let public_inputs = vec![root, external_nullifier, x, y, internal_nullifier];
|
|
|
|
|
+
|
|
|
|
|
+ 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();
|
|
|
|
|
+ print!("[{:?}] ", now.elapsed());
|
|
|
|
|
+ println!("({} bytes)", proof.as_ref().len());
|
|
|
|
|
+
|
|
|
|
|
+ // ============
|
|
|
|
|
+ // Verification
|
|
|
|
|
+ // ============
|
|
|
|
|
+ print!("[Signal] Verifying ZK proof for 0:0... ");
|
|
|
|
|
+ let now = Instant::now();
|
|
|
|
|
+ assert!(proof.verify(&signal_vk, &public_inputs).is_ok());
|
|
|
|
|
+ println!("[{:?}]", now.elapsed());
|
|
|
|
|
+
|
|
|
|
|
+ // Each user of the protocol must store metadata for each message
|
|
|
|
|
+ // received by each user, for the given epoch. The data can be
|
|
|
|
|
+ // deleted when the epoch passes.
|
|
|
|
|
+ let mut metadata = MessageMetadata::new();
|
|
|
|
|
+ if metadata.is_duplicate(&external_nullifier, &internal_nullifier, &x, &y) {
|
|
|
|
|
+ println!("[Signal] Duplicate Message!");
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add share
|
|
|
|
|
+ metadata.add_share(external_nullifier, internal_nullifier, x, y);
|
|
|
|
|
+
|
|
|
|
|
+ // Now let's try to send another message in the same epoch.
|
|
|
|
|
+ // id0 has a limit of 2 so it should pass since the ZK circuit will
|
|
|
|
|
+ // allow this.
|
|
|
|
|
+ let message_id = message_id + pallas::Base::from(1);
|
|
|
|
|
+ let a_0 = poseidon_hash([id0.identity_nullifier, id0.identity_trapdoor]);
|
|
|
|
|
+ let a_1 = poseidon_hash([a_0, external_nullifier, message_id]);
|
|
|
|
|
+ let x = hash_message("hello again");
|
|
|
|
|
+ let y = a_0 + x * a_1;
|
|
|
|
|
+
|
|
|
|
|
+ let internal_nullifier = poseidon_hash([a_1]);
|
|
|
|
|
+
|
|
|
|
|
+ // Skip the inclusion proof for the demo since we have it above.
|
|
|
|
|
+ // Make the ZK proof.
|
|
|
|
|
+ let witnesses = vec![
|
|
|
|
|
+ Witness::Base(Value::known(id0.identity_nullifier)),
|
|
|
|
|
+ Witness::Base(Value::known(id0.identity_trapdoor)),
|
|
|
|
|
+ Witness::Base(Value::known(id0.user_message_limit)),
|
|
|
|
|
+ Witness::SparseMerklePath(Value::known(path.path)),
|
|
|
|
|
+ Witness::Base(Value::known(x)),
|
|
|
|
|
+ Witness::Base(Value::known(message_id)),
|
|
|
|
|
+ Witness::Base(Value::known(epoch)),
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ let public_inputs = vec![root, external_nullifier, x, y, internal_nullifier];
|
|
|
|
|
+
|
|
|
|
|
+ print!("[Signal] Creating ZK proof for 0:1... ");
|
|
|
|
|
+ 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();
|
|
|
|
|
+ print!("[{:?}] ", now.elapsed());
|
|
|
|
|
+ println!("({} bytes)", proof.as_ref().len());
|
|
|
|
|
+
|
|
|
|
|
+ print!("[Signal] Verifying ZK proof for 0:1... ");
|
|
|
|
|
+ let now = Instant::now();
|
|
|
|
|
+ assert!(proof.verify(&signal_vk, &public_inputs).is_ok());
|
|
|
|
|
+ println!("[{:?}]", now.elapsed());
|
|
|
|
|
+
|
|
|
|
|
+ // Each user of the protocol must store metadata for each message
|
|
|
|
|
+ // received by each user, for the given epoch. The data can be
|
|
|
|
|
+ // deleted when the epoch passes.
|
|
|
|
|
+ if metadata.is_duplicate(&external_nullifier, &internal_nullifier, &x, &y) {
|
|
|
|
|
+ println!("[Signal] Duplicate Message!");
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add share
|
|
|
|
|
+ metadata.add_share(external_nullifier, internal_nullifier, x, y);
|
|
|
|
|
+
|
|
|
|
|
+ // Now we shouldn't be able to create more proofs unless we reuse message_id.
|
|
|
|
|
+ // This means that some internal_nullifier will have >1 shares, and it should
|
|
|
|
|
+ // be possible to recover the secret.
|
|
|
|
|
+ // We reuse the above, just try a different message.
|
|
|
|
|
+ let x = hash_message("hello again, i'm reusing a message_id");
|
|
|
|
|
+ let y = a_0 + x * a_1;
|
|
|
|
|
+
|
|
|
|
|
+ // ZK proof:
|
|
|
|
|
+ let witnesses = vec![
|
|
|
|
|
+ Witness::Base(Value::known(id0.identity_nullifier)),
|
|
|
|
|
+ Witness::Base(Value::known(id0.identity_trapdoor)),
|
|
|
|
|
+ Witness::Base(Value::known(id0.user_message_limit)),
|
|
|
|
|
+ Witness::SparseMerklePath(Value::known(path.path)),
|
|
|
|
|
+ Witness::Base(Value::known(x)),
|
|
|
|
|
+ Witness::Base(Value::known(message_id)),
|
|
|
|
|
+ Witness::Base(Value::known(epoch)),
|
|
|
|
|
+ ];
|
|
|
|
|
+ let public_inputs = vec![root, external_nullifier, x, y, internal_nullifier];
|
|
|
|
|
+
|
|
|
|
|
+ print!("[Signal] Creating ZK proof for 0:1 (reused message_id) ... ");
|
|
|
|
|
+ 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();
|
|
|
|
|
+ print!("[{:?}] ", now.elapsed());
|
|
|
|
|
+ println!("({} bytes)", proof.as_ref().len());
|
|
|
|
|
+
|
|
|
|
|
+ print!("[Signal] Verifying ZK proof for 0:1 (reused message_id) ... ");
|
|
|
|
|
+ let now = Instant::now();
|
|
|
|
|
+ assert!(proof.verify(&signal_vk, &public_inputs).is_ok());
|
|
|
|
|
+ println!("[{:?}]", now.elapsed());
|
|
|
|
|
+
|
|
|
|
|
+ // Add share
|
|
|
|
|
+ metadata.add_share(external_nullifier, internal_nullifier, x, y);
|
|
|
|
|
+
|
|
|
|
|
+ // Now the internal_nullifier should have been repeated, and the internal
|
|
|
|
|
+ // nullifier should have 2 (or more) shares.
|
|
|
|
|
+ // Let's recover them.
|
|
|
|
|
+ let shares = metadata.get_shares(&external_nullifier, &internal_nullifier);
|
|
|
|
|
+ println!("{:#?}", shares);
|
|
|
|
|
+ let secret = sss_recover(&shares);
|
|
|
|
|
+ println!("secret: {:?}", secret);
|
|
|
|
|
+ println!("a_0: {:?}", a_0);
|
|
|
|
|
+ assert_eq!(secret, a_0);
|
|
|
|
|
+
|
|
|
|
|
+ // Additionally, it should not be possible to produce (or verify) a ZK
|
|
|
|
|
+ // proof that exceeds the set message limit for an identity.
|
|
|
|
|
+ let message_id = message_id + pallas::Base::from(2);
|
|
|
|
|
+ let a_0 = poseidon_hash([id0.identity_nullifier, id0.identity_trapdoor]);
|
|
|
|
|
+ let a_1 = poseidon_hash([a_0, external_nullifier, message_id]);
|
|
|
|
|
+ let x = hash_message("hello again");
|
|
|
|
|
+ let y = a_0 + x * a_1;
|
|
|
|
|
+
|
|
|
|
|
+ let internal_nullifier = poseidon_hash([a_1]);
|
|
|
|
|
+
|
|
|
|
|
+ // Skip the inclusion proof for the demo since we have it above.
|
|
|
|
|
+ // Make the ZK proof.
|
|
|
|
|
+ let witnesses = vec![
|
|
|
|
|
+ Witness::Base(Value::known(id0.identity_nullifier)),
|
|
|
|
|
+ Witness::Base(Value::known(id0.identity_trapdoor)),
|
|
|
|
|
+ Witness::Base(Value::known(id0.user_message_limit)),
|
|
|
|
|
+ Witness::SparseMerklePath(Value::known(path.path)),
|
|
|
|
|
+ Witness::Base(Value::known(x)),
|
|
|
|
|
+ Witness::Base(Value::known(message_id)),
|
|
|
|
|
+ Witness::Base(Value::known(epoch)),
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ let public_inputs = vec![root, external_nullifier, x, y, internal_nullifier];
|
|
|
|
|
+
|
|
|
|
|
+ println!("[Signal] Creating ZK proof for 0:2 msgid={:?}... ", message_id);
|
|
|
|
|
+ let signal_circuit = ZkCircuit::new(witnesses, &signal_zkbin);
|
|
|
|
|
+ let proof = Proof::create(&signal_pk, &[signal_circuit], &public_inputs, &mut OsRng).unwrap();
|
|
|
|
|
+ assert!(proof.verify(&signal_vk, &public_inputs).is_err());
|
|
|
|
|
+ println!("[Signal] ZK proof for 0:2 failed as expected");
|
|
|
}
|
|
}
|