mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-08 22:28:12 -05:00
research/rln: Add RLN-V2 POC
This commit is contained in:
2
script/research/rln/rlnv2/.gitignore
vendored
Normal file
2
script/research/rln/rlnv2/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
Cargo.lock
|
||||
target/
|
||||
19
script/research/rln/rlnv2/Cargo.toml
Normal file
19
script/research/rln/rlnv2/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "rlnv2"
|
||||
version = "0.4.1"
|
||||
authors = ["Dyne.org foundation <foundation@dyne.org>"]
|
||||
license = "AGPL-3.0-only"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
darkfi-sdk = {path = "../../../../src/sdk"}
|
||||
darkfi = {path = "../../../../", features = ["zk"]}
|
||||
lazy_static = "1.4.0"
|
||||
rand = "0.8.5"
|
||||
blake3 = "1.5.0"
|
||||
|
||||
[patch.crates-io]
|
||||
halo2_proofs = {git="https://github.com/parazyd/halo2", branch="v4"}
|
||||
halo2_gadgets = {git="https://github.com/parazyd/halo2", branch="v4"}
|
||||
26
script/research/rln/rlnv2/Makefile
Normal file
26
script/research/rln/rlnv2/Makefile
Normal file
@@ -0,0 +1,26 @@
|
||||
.POSIX:
|
||||
|
||||
# Cargo binary
|
||||
CARGO = cargo +nightly
|
||||
|
||||
# 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
|
||||
44
script/research/rln/rlnv2/signal.zk
Normal file
44
script/research/rln/rlnv2/signal.zk
Normal file
@@ -0,0 +1,44 @@
|
||||
k = 13;
|
||||
field = "pallas";
|
||||
|
||||
constant "RlnSignal" {}
|
||||
|
||||
witness "RlnSignal" {
|
||||
Base identity_nullifier,
|
||||
Base identity_trapdoor,
|
||||
|
||||
MerklePath identity_path,
|
||||
Uint32 identity_leaf_pos,
|
||||
|
||||
Base x, # The message hash
|
||||
Base message_id,
|
||||
Base message_limit,
|
||||
|
||||
# These are public
|
||||
Base epoch,
|
||||
Base rln_identifier,
|
||||
}
|
||||
|
||||
circuit "RlnSignal" {
|
||||
constrain_instance(message_limit);
|
||||
constrain_instance(epoch);
|
||||
|
||||
external_nullifier = poseidon_hash(epoch, rln_identifier);
|
||||
constrain_instance(external_nullifier);
|
||||
|
||||
a_0 = poseidon_hash(identity_nullifier, identity_trapdoor);
|
||||
a_1 = poseidon_hash(a_0, external_nullifier, message_id);
|
||||
|
||||
internal_nullifier = poseidon_hash(a_1);
|
||||
constrain_instance(internal_nullifier);
|
||||
|
||||
# y = a_0 + x * a_1
|
||||
x_a_1 = base_mul(x, a_1);
|
||||
y = base_add(a_0, x_a_1);
|
||||
constrain_instance(x);
|
||||
constrain_instance(y);
|
||||
|
||||
identity_commitment = poseidon_hash(a_0);
|
||||
root = merkle_root(identity_leaf_pos, identity_path, identity_commitment);
|
||||
constrain_instance(root);
|
||||
}
|
||||
18
script/research/rln/rlnv2/slash.zk
Normal file
18
script/research/rln/rlnv2/slash.zk
Normal file
@@ -0,0 +1,18 @@
|
||||
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);
|
||||
}
|
||||
171
script/research/rln/rlnv2/src/main.rs
Normal file
171
script/research/rln/rlnv2/src/main.rs
Normal file
@@ -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());
|
||||
}
|
||||
Reference in New Issue
Block a user