diff --git a/Cargo.lock b/Cargo.lock index 38cf08cd4a10..fd87f967b8d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1957,6 +1957,7 @@ dependencies = [ "bs58", "crypto_box", "darkfi", + "darkfi-sdk", "darkfi-serial", "easy-parallel", "futures", diff --git a/bin/darkirc/Cargo.toml b/bin/darkirc/Cargo.toml index cfdc42b0019d..004d69800a26 100644 --- a/bin/darkirc/Cargo.toml +++ b/bin/darkirc/Cargo.toml @@ -9,7 +9,8 @@ homepage = "https://dark.fi" repository = "https://github.com/darkrenaissance/darkfi" [dependencies] -darkfi = {path = "../../", features = ["async-daemonize", "event-graph", "net", "util", "system", "rpc"]} +darkfi = {path = "../../", features = ["async-daemonize", "event-graph", "net", "util", "system", "rpc", "zk"]} +darkfi-sdk = {path = "../../src/sdk", features = ["async"]} darkfi-serial = {path = "../../src/serial", features = ["async"]} libc = "0.2.150" diff --git a/bin/darkirc/Makefile b/bin/darkirc/Makefile index eeea17883540..7daaa5729f8b 100644 --- a/bin/darkirc/Makefile +++ b/bin/darkirc/Makefile @@ -17,9 +17,17 @@ SRC = \ BIN = ../../darkirc +ZKAS = ../../zkas + +ZKSRC = $(shell find proof -type f -name '*.zk') +ZKBIN = $(ZKSRC:=.bin) + all: $(BIN) -$(BIN): $(SRC) +$(ZKBIN): $(ZKAS) $(ZKSRC) + $(ZKAS) $(basename $@) -o $@ + +$(BIN): $(ZKBIN) $(SRC) RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) build --target=$(RUST_TARGET) --release --package darkirc cp -f ../../target/$(RUST_TARGET)/release/darkirc $@ diff --git a/bin/darkirc/proof/signal.zk b/bin/darkirc/proof/signal.zk new file mode 100644 index 000000000000..c089e7f5dd79 --- /dev/null +++ b/bin/darkirc/proof/signal.zk @@ -0,0 +1,38 @@ +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/bin/darkirc/proof/slash.zk b/bin/darkirc/proof/slash.zk new file mode 100644 index 000000000000..19bc0f192155 --- /dev/null +++ b/bin/darkirc/proof/slash.zk @@ -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); +} diff --git a/bin/darkirc/src/main.rs b/bin/darkirc/src/main.rs index ac67503879e6..cbaff8040762 100644 --- a/bin/darkirc/src/main.rs +++ b/bin/darkirc/src/main.rs @@ -45,6 +45,9 @@ use irc::server::IrcServer; /// Cryptography utilities mod crypto; +// RLN +//mod rln; + /// JSON-RPC methods mod rpc; @@ -108,7 +111,7 @@ struct Args { pub struct DarkIrc { /// P2P network pointer p2p: P2pPtr, - /// Sled DB (also used in event_graph) + /// Sled DB (also used in event_graph and for RLN) sled: sled::Db, /// Event Graph instance event_graph: EventGraphPtr, diff --git a/bin/darkirc/src/rln.rs b/bin/darkirc/src/rln.rs new file mode 100644 index 000000000000..3235044265a6 --- /dev/null +++ b/bin/darkirc/src/rln.rs @@ -0,0 +1,113 @@ +/* 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 . + */ + +//! + +use darkfi::{ + zk::{empty_witnesses, halo2::Field, ProvingKey, VerifyingKey, ZkCircuit}, + zkas::ZkBinary, + Result, +}; +use darkfi_sdk::{crypto::MerkleTree, pasta::pallas}; +use darkfi_serial::serialize_async; +use log::info; + +const RLN_IDENTIFIER: pallas::Base = pallas::Base::from_raw([0, 0, 42, 42]); +const IDENTITY_DERIVATION_PATH: pallas::Base = pallas::Base::from_raw([0, 0, 42, 11]); +const NULLIFIER_DERIVATION_PATH: pallas::Base = pallas::Base::from_raw([0, 0, 42, 12]); + +/// Rate-Limit-Nullifiers +/// +/// This mechanism is used for spam protection on the IRC network. +pub struct Rln { + /// DB holding identity commitments and the membership Merkle tree + /// The scheme is `(k=identity_commitment, v=leaf_position)` + identities: sled::Tree, + /// DB holding identity roots + identity_roots: sled::Tree, + /// DB holding banned roots + banned_roots: sled::Tree, + /// Proving key for the signalling circuit + signal_pk: ProvingKey, + /// Verifying key for the signalling circuit + signal_vk: VerifyingKey, + /// Proving key for the slashing circuit + slash_pk: ProvingKey, + /// Verifying key for the slashing circuit + slash_vk: VerifyingKey, +} + +impl Rln { + /// Create a new Rln instance + pub async fn new(sled_db: &sled::Db) -> Result { + let identities = sled_db.open_tree("identities")?; + let identity_roots = sled_db.open_tree("identity_roots")?; + let banned_roots = sled_db.open_tree("banned_roots")?; + + if !identities.contains_key(b"identity_tree")? { + info!("Creating RLN membership tree"); + let membership_tree = MerkleTree::new(100); + identities.insert(b"identity_tree", serialize_async(&membership_tree).await)?; + } + + let signal_zkbin = include_bytes!("../proof/signal.zk.bin"); + let slash_zkbin = include_bytes!("../proof/slash.zk.bin"); + + info!("Building RLN signal proving key"); + let signal_zkbin = ZkBinary::decode(signal_zkbin).unwrap(); + let signal_circuit = ZkCircuit::new(empty_witnesses(&signal_zkbin)?, &signal_zkbin); + let signal_pk = ProvingKey::build(signal_zkbin.k, &signal_circuit); + info!("Building RLN signal verifying key"); + let signal_vk = VerifyingKey::build(signal_zkbin.k, &signal_circuit); + + info!("Building RLN slash proving key"); + let slash_zkbin = ZkBinary::decode(slash_zkbin).unwrap(); + let slash_circuit = ZkCircuit::new(empty_witnesses(&slash_zkbin)?, &slash_zkbin); + let slash_pk = ProvingKey::build(slash_zkbin.k, &slash_circuit); + info!("Building RLN slash verifying key"); + let slash_vk = VerifyingKey::build(slash_zkbin.k, &slash_circuit); + + Ok(Self { + identities, + identity_roots, + banned_roots, + signal_pk, + signal_vk, + slash_pk, + slash_vk, + }) + } + + /// Recover a secret from given secret shares + pub 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 + } +}