diff --git a/script/research/rln/rlnv1/.gitignore b/script/research/rln/rlnv1/.gitignore deleted file mode 100644 index 1e7caa9ea..000000000 --- a/script/research/rln/rlnv1/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -Cargo.lock -target/ diff --git a/script/research/rln/rlnv1/Cargo.toml b/script/research/rln/rlnv1/Cargo.toml deleted file mode 100644 index d21e498c2..000000000 --- a/script/research/rln/rlnv1/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "rlnv1" -version = "0.4.1" -authors = ["Dyne.org foundation "] -license = "AGPL-3.0-only" -edition = "2021" - -[workspace] - -[dependencies] -darkfi-sdk = {path = "../../../../src/sdk"} -darkfi = {path = "../../../../", features = ["zk"]} -lazy_static = "1.5.0" -rand = "0.8.5" - -[patch.crates-io] -halo2_proofs = {git="https://github.com/parazyd/halo2", branch="v4"} -halo2_gadgets = {git="https://github.com/parazyd/halo2", branch="v4"} diff --git a/script/research/rln/rlnv1/Makefile b/script/research/rln/rlnv1/Makefile deleted file mode 100644 index c487c7f26..000000000 --- a/script/research/rln/rlnv1/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -.POSIX: - -# Cargo binary -CARGO = cargo - -# Compile target -RUST_TARGET = $(shell rustc -Vv | grep '^host: ' | cut -d' ' -f2) - -PROOFS_SRC = signal.zk slash.zk -PROOFS_BIN = $(PROOFS_SRC:=.bin) - -ZKAS = ../../../../zkas - -all: $(PROOFS_BIN) - $(CARGO) run --target=$(RUST_TARGET) --release - -$(ZKAS): - $(MAKE) -C ../../../../zkas - -$(PROOFS_BIN): $(ZKAS) $(PROOFS_SRC) - $(ZKAS) $(basename $@) -o $@ - -clean: - rm -rf target $(PROOFS_BIN) Cargo.lock - -.PHONY: all clean diff --git a/script/research/rln/rlnv1/signal.zk b/script/research/rln/rlnv1/signal.zk deleted file mode 100644 index 8587c7997..000000000 --- a/script/research/rln/rlnv1/signal.zk +++ /dev/null @@ -1,38 +0,0 @@ -k = 13; -field = "pallas"; - -constant "RlnSignal" {} - -witness "RlnSignal" { - Base secret_key, - MerklePath identity_path, - Uint32 identity_leaf_pos, - - # These are public so have to be properly constructed - Base message_hash, # x - Base epoch, - Base rln_identifier, -} - -circuit "RlnSignal" { - constrain_instance(epoch); - constrain_instance(rln_identifier); - constrain_instance(message_hash); - - # This has to be the same constant used outside - identity_derivation_path = witness_base(11); - nullifier_derivation_path = witness_base(12); - - identity_commit = poseidon_hash(identity_derivation_path, secret_key); - root = merkle_root(identity_leaf_pos, identity_path, identity_commit); - constrain_instance(root); - - external_nullifier = poseidon_hash(epoch, rln_identifier); - a_1 = poseidon_hash(secret_key, external_nullifier); - internal_nullifier = poseidon_hash(nullifier_derivation_path, a_1); - constrain_instance(internal_nullifier); - - y_a = base_mul(a_1, message_hash); - y = base_add(y_a, secret_key); - constrain_instance(y); -} diff --git a/script/research/rln/rlnv1/slash.zk b/script/research/rln/rlnv1/slash.zk deleted file mode 100644 index 19bc0f192..000000000 --- a/script/research/rln/rlnv1/slash.zk +++ /dev/null @@ -1,18 +0,0 @@ -k = 13; -field = "pallas"; - -constant "RlnSlash" {} - -witness "RlnSlash" { - Base secret_key, - MerklePath identity_path, - Uint32 identity_leaf_pos, -} - -circuit "RlnSlash" { - identity_derivation_path = witness_base(11); - - identity_commit = poseidon_hash(identity_derivation_path, secret_key); - root = merkle_root(identity_leaf_pos, identity_path, identity_commit); - constrain_instance(root); -} diff --git a/script/research/rln/rlnv1/src/main.rs b/script/research/rln/rlnv1/src/main.rs deleted file mode 100644 index 662b90678..000000000 --- a/script/research/rln/rlnv1/src/main.rs +++ /dev/null @@ -1,259 +0,0 @@ -/* This file is part of DarkFi (https://dark.fi) - * - * Copyright (C) 2020-2025 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 . - */ - -//! - -use std::{collections::HashMap, time::Instant}; - -use darkfi::{ - zk::{empty_witnesses, halo2::Value, Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit}, - zkas::ZkBinary, -}; -use darkfi_sdk::{ - crypto::{pasta_prelude::*, poseidon_hash, MerkleNode, MerkleTree}, - pasta::{Ep, Fp}, -}; -use lazy_static::lazy_static; -use rand::rngs::OsRng; - -// These should be unique constants per application. -lazy_static! { - static ref RLN_IDENTIFIER: Fp = Fp::from(42); - static ref IDENTITY_DERIVATION_PATH: Fp = Fp::from(11); - static ref NULLIFIER_DERIVATION_PATH: Fp = Fp::from(12); -} - -fn hash_message(message: &[u8]) -> Fp { - let hasher = Ep::hash_to_curve("rln-domain:demoapp"); - let message_point = hasher(message); - let message_coords = message_point.to_affine().coordinates().unwrap(); - poseidon_hash([*message_coords.x(), *message_coords.y()]) -} - -fn sss_recover(shares: &[(Fp, Fp)]) -> Fp { - let mut secret = Fp::zero(); - for (j, share_j) in shares.iter().enumerate() { - let mut prod = Fp::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() { - let epoch = Fp::from(1674509414); - let external_nullifier = poseidon_hash([epoch, *RLN_IDENTIFIER]); - - // The identity commitment should be something that cannot be - // precalculated for usage in the future, and possibly also has - // to be some kind of puzzle that is costly to (pre)calculate. - // Alternatively, it could be economic stake of funds which could - // then be lost if spam is detected and acted upon. - let secret_key = Fp::random(&mut OsRng); - let identity_commitment = poseidon_hash([*IDENTITY_DERIVATION_PATH, secret_key]); - - // ============ - // Registration - // ============ - let mut membership_tree = MerkleTree::new(1); - let mut identity_roots: Vec = vec![]; - let mut banned_roots: Vec = vec![]; - let mut identities = HashMap::new(); - - // Everyone needs to maintain the leaf positions, because to slash, we - // need to provide a valid authentication path. Therefore, the easiest - // way is to store a hashmap. - assert!(!identities.contains_key(&identity_commitment.to_repr())); - membership_tree.append(MerkleNode::from(identity_commitment)); - let leaf_pos = membership_tree.mark().unwrap(); - identities.insert(identity_commitment.to_repr(), leaf_pos); - identity_roots.push(membership_tree.root(0).unwrap()); - - // ========== - // Signalling - // ========== - let a_1 = poseidon_hash([secret_key, external_nullifier]); - - // Construct share - let x = hash_message(b"hello i wanna spam"); - let y = a_1 * x + secret_key; - - // Construct internal nullifier - let internal_nullifier = poseidon_hash([*NULLIFIER_DERIVATION_PATH, a_1]); - - let identity_root = membership_tree.root(0).unwrap(); - let identity_path = membership_tree.witness(leaf_pos, 0).unwrap(); - - // zkSNARK things - let signal_zkbin = include_bytes!("../signal.zk.bin"); - let rln_zkbin = ZkBinary::decode(signal_zkbin).unwrap(); - let rln_empty_circuit = ZkCircuit::new(empty_witnesses(&rln_zkbin).unwrap(), &rln_zkbin); - - print!("[Interaction] Building Proving key... "); - let now = Instant::now(); - let rln_pk = ProvingKey::build(13, &rln_empty_circuit); - println!("[{:?}]", now.elapsed()); - - print!("[Interaction] Building Verifying key... "); - let now = Instant::now(); - let rln_vk = VerifyingKey::build(13, &rln_empty_circuit); - println!("[{:?}]", now.elapsed()); - - // Prover's witnesses and public inputs - let witnesses = vec![ - Witness::Base(Value::known(secret_key)), - Witness::MerklePath(Value::known(identity_path.clone().try_into().unwrap())), - Witness::Uint32(Value::known(u64::from(leaf_pos).try_into().unwrap())), - Witness::Base(Value::known(x)), - Witness::Base(Value::known(epoch)), - Witness::Base(Value::known(*RLN_IDENTIFIER)), - ]; - - let public_inputs = vec![ - epoch, - *RLN_IDENTIFIER, - x, // <-- Message hash - identity_root.inner(), - internal_nullifier, - y, - ]; - - // Build a circuit with these witnesses - print!("[Interaction] Creating ZK proof... "); - let now = Instant::now(); - let rln_circuit = ZkCircuit::new(witnesses, &rln_zkbin); - let proof = Proof::create(&rln_pk, &[rln_circuit], &public_inputs, &mut OsRng).unwrap(); - println!("[{:?}]", now.elapsed()); - - // ============ - // Verification - // ============ - print!("[Interaction] Verifying ZK proof... "); - let now = Instant::now(); - assert!(proof.verify(&rln_vk, &public_inputs).is_ok()); - assert!(!banned_roots.contains(&MerkleNode::from(public_inputs[3]))); - assert!(identity_roots.contains(&MerkleNode::from(public_inputs[3]))); - println!("[{:?}]", now.elapsed()); - - // NOTE: These shares should actually be tracked through the internal nullifier. - let mut shares = vec![(public_inputs[2], public_inputs[5])]; - - // Now if another message is sent in the same epoch, we should be able to - // recover the secret key and ban the sender. - let x = hash_message(b"hello i'm spamming"); - let y = a_1 * x + secret_key; - - // Same epoch and account, different message - let witnesses = vec![ - Witness::Base(Value::known(secret_key)), - Witness::MerklePath(Value::known(identity_path.try_into().unwrap())), - Witness::Uint32(Value::known(u64::from(leaf_pos).try_into().unwrap())), - Witness::Base(Value::known(x)), - Witness::Base(Value::known(epoch)), - Witness::Base(Value::known(*RLN_IDENTIFIER)), - ]; - - let public_inputs = vec![ - epoch, - *RLN_IDENTIFIER, - x, // <-- Message hash - identity_root.inner(), - internal_nullifier, - y, - ]; - - // Build a circuit with these witnesses - print!("[Interaction] Creating ZK proof... "); - let now = Instant::now(); - let rln_circuit = ZkCircuit::new(witnesses, &rln_zkbin); - let proof = Proof::create(&rln_pk, &[rln_circuit], &public_inputs, &mut OsRng).unwrap(); - println!("[{:?}]", now.elapsed()); - - print!("[Interaction] Verifying ZK proof... "); - let now = Instant::now(); - assert!(proof.verify(&rln_vk, &public_inputs).is_ok()); - assert!(!banned_roots.contains(&MerkleNode::from(public_inputs[3]))); - assert!(identity_roots.contains(&MerkleNode::from(public_inputs[3]))); - println!("[{:?}]", now.elapsed()); - - // NOTE: These shares should actually be tracked through the internal nullifier. - shares.push((public_inputs[2], public_inputs[5])); - - // ======== - // Slashing - // ======== - - // We should be able to retrieve the secret key because two messages were - // sent in the same epoch. - let recovered_secret = sss_recover(&shares); - assert_eq!(recovered_secret, secret_key); - - // Create a slash proof - let slash_zkbin = include_bytes!("../slash.zk.bin"); - let slash_zkbin = ZkBinary::decode(slash_zkbin).unwrap(); - let slash_empty_circuit = ZkCircuit::new(empty_witnesses(&slash_zkbin).unwrap(), &slash_zkbin); - - print!("[Slash] Building Proving key... "); - let now = Instant::now(); - let slash_pk = ProvingKey::build(13, &slash_empty_circuit); - println!("[{:?}]", now.elapsed()); - - print!("[Slash] Building Verifying key... "); - let now = Instant::now(); - let slash_vk = VerifyingKey::build(13, &slash_empty_circuit); - println!("[{:?}]", now.elapsed()); - - // Find the leaf position in the hashmap of identity commitments - let identity_commitment = poseidon_hash([*IDENTITY_DERIVATION_PATH, recovered_secret]); - let leaf_pos = identities.get(&identity_commitment.to_repr()).unwrap(); - let identity_root = membership_tree.root(0).unwrap(); - let identity_path = membership_tree.witness(*leaf_pos, 0); - let identity_path = identity_path.unwrap(); - - // Witnesses & public inputs - let witnesses = vec![ - Witness::Base(Value::known(recovered_secret)), - Witness::MerklePath(Value::known(identity_path.try_into().unwrap())), - Witness::Uint32(Value::known(u64::from(*leaf_pos).try_into().unwrap())), - ]; - - let public_inputs = vec![identity_root.inner()]; - - print!("[Slash] Creating ZK proof... "); - let now = Instant::now(); - let slash_circuit = ZkCircuit::new(witnesses, &slash_zkbin); - let proof = Proof::create(&slash_pk, &[slash_circuit], &public_inputs, &mut OsRng).unwrap(); - println!("[{:?}]", now.elapsed()); - - print!("[Slash] Verifying ZK proof... "); - let now = Instant::now(); - assert!(!banned_roots.contains(&MerkleNode::from(public_inputs[0]))); - assert!(identity_roots.contains(&MerkleNode::from(public_inputs[0]))); // <- Will this be true? - assert!(proof.verify(&slash_vk, &public_inputs).is_ok()); - println!("[{:?}]", now.elapsed()); - banned_roots.push(MerkleNode::from(public_inputs[0])); - - println!("boi u banned"); -}