From 8f3ece308d920864b1614e781235e9ce77c178e7 Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 12 Sep 2024 20:56:19 +0900 Subject: [PATCH 01/12] =?UTF-8?q?=E2=9E=95=20Add=20nalgebra=20crate=20into?= =?UTF-8?q?=20dependencies.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a32cc8c..f695453d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "ark-bls12-377" version = "0.3.0" @@ -324,6 +333,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytemuck" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" + [[package]] name = "cast" version = "0.3.0" @@ -685,6 +700,16 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "matrixmultiply" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "memchr" version = "2.7.2" @@ -741,6 +766,33 @@ dependencies = [ name = "mpc-trait" version = "0.1.0" +[[package]] +name = "nalgebra" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c4b5f057b303842cf3262c27e465f4c303572e7f6b0648f60e16248ac3397f4" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -753,6 +805,15 @@ dependencies = [ "rand", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -763,11 +824,22 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -935,6 +1007,12 @@ dependencies = [ "rand", ] +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rayon" version = "1.8.0" @@ -999,6 +1077,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "safe_arch" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -1076,6 +1163,19 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "simba" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + [[package]] name = "strsim" version = "0.8.0" @@ -1358,6 +1458,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wide" +version = "0.7.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1575,6 +1685,7 @@ dependencies = [ "mpc-algebra", "mpc-net", "mpc-trait", + "nalgebra", "num-bigint", "num-integer", "num-traits", diff --git a/Cargo.toml b/Cargo.toml index ce19f194..a058c575 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ mpc-algebra = { path = "mpc-algebra", version = "0.1.0" } mpc-net = { path = "mpc-net", version = "0.1.0" } mpc-trait = { path = "mpc-trait", version = "0.1.0" } itertools = "0.13.0" +nalgebra = "0.33.0" [dev-dependencies] criterion = { version = "0.4", features = ["html_reports"] } From 4d8140e56b5d7a8abe00218b466e72fde0ca38b6 Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 12 Sep 2024 21:00:08 +0900 Subject: [PATCH 02/12] =?UTF-8?q?=E2=9C=A8=20Add=20Grouping=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/bin_werewolf.rs | 54 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/examples/bin_werewolf.rs b/examples/bin_werewolf.rs index ed2d7753..34dc5cbf 100644 --- a/examples/bin_werewolf.rs +++ b/examples/bin_werewolf.rs @@ -12,6 +12,7 @@ use mpc_algebra::malicious_majority::*; use mpc_algebra::Reveal; use mpc_net::{MpcMultiNet as Net, MpcNet}; use serde::Deserialize; +use std::collections::HashMap; use std::{fs::File, path::PathBuf}; use structopt::StructOpt; use zk_mpc::circuits::{ @@ -28,6 +29,8 @@ use zk_mpc::preprocessing; use zk_mpc::serialize::{write_r, write_to_file}; use zk_mpc::she; +use nalgebra as na; + #[derive(Debug, StructOpt)] struct Opt { /// Run mode @@ -288,6 +291,55 @@ fn preprocessing_werewolf(opt: &Opt) -> Result<(), std::io::Error> { Ok(()) } +struct GroupingParameter(HashMap); + +impl GroupingParameter { + fn new(input: HashMap) -> Self { + Self(input) + } + + fn generate_tau_matrix(&self) -> na::DMatrix { + todo!() + } + + fn get_num_roles(&self) -> usize { + self.0.len() + } + + fn get_num_groups(&self) -> usize { + self.0 + .values() + .map(|(count, is_not_alone)| if *is_not_alone { 1 } else { *count }) + .sum() + } + + fn get_num_players(&self) -> usize { + self.0.values().map(|x| x.0).sum() + } +} + +#[test] +fn test_grouping_parameter() { + let grouping_parameter = GroupingParameter::new( + vec![ + (Roles::Villager, (4, false)), + (Roles::FortuneTeller, (1, false)), + (Roles::Werewolf, (2, true)), + ] + .into_iter() + .collect(), + ); + + // Villager, FortuneTeller, Werewolf + assert_eq!(grouping_parameter.get_num_roles(), 3); + + // Villager: 1, 2, 3, 4, FortuneTeller: 1, Werewolfs: 1 + assert_eq!(grouping_parameter.get_num_groups(), 6); + + // Total 4 + 1 + 2 = 7 + assert_eq!(grouping_parameter.get_num_players(), 7); +} + fn night_werewolf(opt: &Opt) -> Result<(), std::io::Error> { // init Net::init_from_file( @@ -303,7 +355,7 @@ fn night_werewolf(opt: &Opt) -> Result<(), std::io::Error> { Ok(()) } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq, Hash)] enum Roles { FortuneTeller, Werewolf, From b7253bb9c2c51f55954947846bb6f6f177358197 Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Tue, 17 Sep 2024 15:05:19 +0900 Subject: [PATCH 03/12] =?UTF-8?q?=E2=9C=A8=20Implement=20generate=20initia?= =?UTF-8?q?l=20role=20permutation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/bin_werewolf.rs | 50 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/examples/bin_werewolf.rs b/examples/bin_werewolf.rs index 34dc5cbf..1ff99047 100644 --- a/examples/bin_werewolf.rs +++ b/examples/bin_werewolf.rs @@ -6,12 +6,16 @@ use ark_marlin::IndexProverKey; use ark_mnt4_753::FqParameters; use ark_serialize::{CanonicalDeserialize, Read}; use ark_std::test_rng; +use ark_std::One; +use ark_std::Zero; +use core::num; use core::panic; use mpc_algebra::encryption::elgamal::elgamal::Parameters; use mpc_algebra::malicious_majority::*; use mpc_algebra::Reveal; use mpc_net::{MpcMultiNet as Net, MpcNet}; use serde::Deserialize; +use std::collections::BTreeMap; use std::collections::HashMap; use std::{fs::File, path::PathBuf}; use structopt::StructOpt; @@ -291,15 +295,53 @@ fn preprocessing_werewolf(opt: &Opt) -> Result<(), std::io::Error> { Ok(()) } -struct GroupingParameter(HashMap); +struct GroupingParameter(BTreeMap); impl GroupingParameter { - fn new(input: HashMap) -> Self { + fn new(input: BTreeMap) -> Self { Self(input) } fn generate_tau_matrix(&self) -> na::DMatrix { - todo!() + let num_players = self.get_num_players(); + let num_groups = self.get_num_groups(); + + let mut tau = na::DMatrix::::zeros(num_players + num_groups, num_players + num_groups); + + let mut player_index = 0; + let mut group_index = 0; + + for (_, (count, is_not_alone)) in self.0.iter() { + if *is_not_alone { + assert!( + *count >= 2, + "Error: not alone group count must be greater than 2" + ); + + // group + tau[(player_index, num_players + group_index)] = MFr::one(); + + // player + for _ in 0..*count - 1 { + tau[(player_index + 1, player_index)] = MFr::one(); + player_index += 1; + } + tau[(num_players + group_index, player_index)] = MFr::one(); + player_index += 1; + group_index += 1; + } else { + for _ in 0..*count { + // group + tau[(player_index, num_players + group_index)] = MFr::one(); + // player + tau[(num_players + group_index, player_index)] = MFr::one(); + player_index += 1; + group_index += 1; + } + } + } + + tau } fn get_num_roles(&self) -> usize { @@ -355,7 +397,7 @@ fn night_werewolf(opt: &Opt) -> Result<(), std::io::Error> { Ok(()) } -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] enum Roles { FortuneTeller, Werewolf, From d42e34cea9aa171624e43bf06f9df84c2ca73e66 Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Tue, 17 Sep 2024 15:44:45 +0900 Subject: [PATCH 04/12] =?UTF-8?q?=E2=9C=A8=20Implement=20shuffle=20matrix?= =?UTF-8?q?=20and=20tests.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/bin_werewolf.rs | 130 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/examples/bin_werewolf.rs b/examples/bin_werewolf.rs index 1ff99047..25b78fc4 100644 --- a/examples/bin_werewolf.rs +++ b/examples/bin_werewolf.rs @@ -14,6 +14,8 @@ use mpc_algebra::encryption::elgamal::elgamal::Parameters; use mpc_algebra::malicious_majority::*; use mpc_algebra::Reveal; use mpc_net::{MpcMultiNet as Net, MpcNet}; +use rand::seq::SliceRandom; +use rand::Rng; use serde::Deserialize; use std::collections::BTreeMap; use std::collections::HashMap; @@ -295,6 +297,120 @@ fn preprocessing_werewolf(opt: &Opt) -> Result<(), std::io::Error> { Ok(()) } +// Compute shuffle matrix and return role and player id in the same group. +fn calc_shuffle_matrix( + grouping_parameter: &GroupingParameter, + shuffle_matrix: &[na::DMatrix], + id: usize, +) -> Result<(Roles, Option>), std::io::Error> { + // parameters + let n = grouping_parameter.get_num_players(); + let m = grouping_parameter.get_num_groups(); + + // generate tau matrix + let tau_matrix = grouping_parameter.generate_tau_matrix(); + + // compute rho matrix + let m_matrix = shuffle_matrix + .iter() + .fold(na::DMatrix::::identity(n + m, n + m), |acc, x| acc * x); + let rho_matrix = m_matrix.transpose() * tau_matrix * m_matrix; + + // iterate. get rho^1, rho^2, ..., rho^num_players + let mut rho_sequence = Vec::with_capacity(n); + let mut current_rho = rho_matrix.clone(); + for _ in 0..n { + rho_sequence.push(current_rho.clone()); + current_rho *= rho_matrix.clone(); // rho^(i+1) = rho^i * rho + } + + let mut unit_vec = na::DVector::::zeros(n + m); + unit_vec[id] = MFr::one(); + + // player i: for each j in {1..n}, get rho^j(i) + let result = rho_sequence + .iter() + .map(|rho| rho * unit_vec.clone()) + .map(|x| { + let index = x.column(0).into_iter().enumerate().find_map(|(j, value)| { + if *value != MFr::zero() { + Some(j) + } else { + None + } + }); + index.unwrap_or_else(|| panic!("Error: No index found")) + }) // search for the index of the one element + .collect::>(); + + println!("player {:?} result is {:?}", id, result); + + // get role value. get val which is max value in result. + let role_val = result.iter().max().expect("Failed to get max value"); + + // get role + let role = grouping_parameter.get_corresponding_role(*role_val); + + let mut fellow = result + .iter() + .filter(|x| **x != id && **x < n) + .copied() + .collect::>(); + + if fellow.is_empty() { + Ok((role, None)) + } else { + fellow.sort(); + fellow.dedup(); + Ok((role, Some(fellow))) + } +} + +fn generate_individual_shuffle_matrix(n: usize, m: usize, rng: &mut R) -> na::DMatrix { + let mut shuffle_matrix = na::DMatrix::::zeros(n + m, n + m); + + // generate permutation + let mut permutation: Vec = (0..n).collect(); + permutation.shuffle(rng); + + // shuffle_matrix + for i in 0..n { + shuffle_matrix[(i, permutation[i])] = MFr::one(); + } + + for i in n..n + m { + shuffle_matrix[(i, i)] = MFr::one(); + } + + shuffle_matrix +} + +#[test] +fn test_shuffle_matrix() { + let grouping_parameter = GroupingParameter::new( + vec![ + (Roles::Villager, (4, false)), + (Roles::FortuneTeller, (1, false)), + (Roles::Werewolf, (2, true)), + ] + .into_iter() + .collect(), + ); + + let shuffle_matrix = vec![generate_individual_shuffle_matrix( + grouping_parameter.get_num_players(), + grouping_parameter.get_num_groups(), + &mut test_rng(), + )]; + + for id in 0..grouping_parameter.get_num_players() { + let (role, player_ids) = + calc_shuffle_matrix(&grouping_parameter, &shuffle_matrix, id).unwrap(); + println!("role is {:?}", role); + println!("fellow is {:?}", player_ids); + } +} + struct GroupingParameter(BTreeMap); impl GroupingParameter { @@ -358,6 +474,18 @@ impl GroupingParameter { fn get_num_players(&self) -> usize { self.0.values().map(|x| x.0).sum() } + + fn get_corresponding_role(&self, role_id: usize) -> Roles { + let mut count = self.get_num_players(); + for (role, (role_count, is_not_alone)) in self.0.iter() { + count += if *is_not_alone { 1 } else { *role_count }; + if role_id < count { + return role.clone(); + } + } + + panic!("Error: Invalid role id is given"); + } } #[test] @@ -397,7 +525,7 @@ fn night_werewolf(opt: &Opt) -> Result<(), std::io::Error> { Ok(()) } -#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] +#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)] enum Roles { FortuneTeller, Werewolf, From ae96ee94b822eb8c4e0544e6dc8ebfc527d20d8d Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 26 Sep 2024 20:09:20 +0900 Subject: [PATCH 05/12] =?UTF-8?q?=E2=9C=A8=20Implement=20role=20assignment?= =?UTF-8?q?=20&=20its=20circuit(partially)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/bin_werewolf.rs | 86 ++++++- run_werewolf.zsh | 17 ++ src/circuits/werewolf.rs | 506 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 599 insertions(+), 10 deletions(-) diff --git a/examples/bin_werewolf.rs b/examples/bin_werewolf.rs index 25b78fc4..855f212f 100644 --- a/examples/bin_werewolf.rs +++ b/examples/bin_werewolf.rs @@ -21,6 +21,7 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::{fs::File, path::PathBuf}; use structopt::StructOpt; +use zk_mpc::circuits::RoleAssignmentCircuit; use zk_mpc::circuits::{ AnonymousVotingCircuit, DivinationCircuit, ElGamalLocalOrMPC, KeyPublicizeCircuit, }; @@ -89,6 +90,12 @@ fn main() -> Result<(), Box> { preprocessing_werewolf(&opt)?; } + "role_assignment" => { + println!("Role assignment mode"); + // role assignment + + role_assignment(&opt)?; + } "night" => { println!("Night mode"); // run the night phase @@ -297,12 +304,83 @@ fn preprocessing_werewolf(opt: &Opt) -> Result<(), std::io::Error> { Ok(()) } -// Compute shuffle matrix and return role and player id in the same group. +fn role_assignment(opt: &Opt) -> Result<(), std::io::Error> { + // init + Net::init_from_file( + opt.input.clone().unwrap().to_str().unwrap(), + opt.id.unwrap(), + ); + + let grouping_parameter = GroupingParameter::new( + vec![ + (Roles::Villager, (3, false)), + (Roles::FortuneTeller, (1, false)), + (Roles::Werewolf, (2, true)), + ] + .into_iter() + .collect(), + ); + + let n = grouping_parameter.get_num_players(); + let m = grouping_parameter.get_num_groups(); + + let rng = &mut test_rng(); + + // calc + let shuffle_matrix = vec![ + generate_individual_shuffle_matrix( + grouping_parameter.get_num_players(), + grouping_parameter.get_num_groups(), + rng, + ); + 2 + ]; + + let mut inputs = vec![]; + + for id in 0..n { + let (role, role_val, player_ids) = + calc_shuffle_matrix(&grouping_parameter, &shuffle_matrix, id).unwrap(); + println!("role is {:?}", role); + println!("fellow is {:?}", player_ids); + inputs.push(Fr::from(role_val as i32)); + } + + println!("inputs is {:?}", inputs); + + // prove + let local_role_circuit = RoleAssignmentCircuit { + num_players: n, + tau_matrix: na::DMatrix::::zeros(n + m, n + m), + result: inputs.clone(), + shuffle_matrices: vec![na::DMatrix::::zeros(n + m, n + m); 2], + }; + + let (mpc_index_pk, index_vk) = setup_and_index(local_role_circuit); + + let mpc_role_circuit = RoleAssignmentCircuit { + num_players: n, + tau_matrix: grouping_parameter.generate_tau_matrix(), + result: inputs.iter().map(|x| MFr::from_public(*x)).collect(), + shuffle_matrices: shuffle_matrix, + }; + + assert!(prove_and_verify( + &mpc_index_pk, + &index_vk, + mpc_role_circuit.clone(), + inputs + )); + + Ok(()) +} + +// Compute shuffle matrix and return role, raw role id, and player id in the same group. fn calc_shuffle_matrix( grouping_parameter: &GroupingParameter, shuffle_matrix: &[na::DMatrix], id: usize, -) -> Result<(Roles, Option>), std::io::Error> { +) -> Result<(Roles, usize, Option>), std::io::Error> { // parameters let n = grouping_parameter.get_num_players(); let m = grouping_parameter.get_num_groups(); @@ -358,11 +436,11 @@ fn calc_shuffle_matrix( .collect::>(); if fellow.is_empty() { - Ok((role, None)) + Ok((role, *role_val, None)) } else { fellow.sort(); fellow.dedup(); - Ok((role, Some(fellow))) + Ok((role, *role_val, Some(fellow))) } } diff --git a/run_werewolf.zsh b/run_werewolf.zsh index 77d86cce..b2d7aaa0 100755 --- a/run_werewolf.zsh +++ b/run_werewolf.zsh @@ -51,6 +51,23 @@ night) fi done + for pid in ${PROCS[@]}; do + wait $pid + done + ;; +role_assignment) + for i in $(seq 0 $((players - 1))); do + if [ $i == 0 ]; then + RUST_BACKTRACE=1 $BIN role_assignment $i ./data/address & + pid=$! + PROCS[$i]=$pid + else + $BIN role_assignment $i ./data/address >/dev/null & + pid=$! + PROCS[$i]=$pid + fi + done + for pid in ${PROCS[@]}; do wait $pid done diff --git a/src/circuits/werewolf.rs b/src/circuits/werewolf.rs index 1ecd7e4e..c5468046 100644 --- a/src/circuits/werewolf.rs +++ b/src/circuits/werewolf.rs @@ -4,7 +4,7 @@ use ark_crypto_primitives::encryption::*; use ark_ec::AffineCurve; use ark_ec::ProjectiveCurve; use ark_ed_on_bls12_377::constraints::EdwardsVar; -use ark_ff::PrimeField; +use ark_ff::{PrimeField, SquareRootField}; use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::boolean::Boolean; use ark_r1cs_std::eq::EqGadget; @@ -12,15 +12,18 @@ use ark_r1cs_std::fields::fp::FpVar; use ark_r1cs_std::fields::FieldVar; use ark_r1cs_std::groups::CurveVar; use ark_r1cs_std::select::CondSelectGadget; -use ark_r1cs_std::ToBitsGadget; +use ark_r1cs_std::{R1CSVar, ToBitsGadget}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use ark_std::{test_rng, One, Zero}; use mpc_algebra::groups::MpcCurveVar; use mpc_algebra::mpc_fields::MpcFieldVar; use mpc_algebra::{ - MpcBoolean, MpcCondSelectGadget, MpcEqGadget, MpcFpVar, MpcToBitsGadget, Reveal, + BitDecomposition, EqualityZero, MpcBoolean, MpcCondSelectGadget, MpcEqGadget, MpcFpVar, + MpcToBitsGadget, Reveal, }; +use nalgebra as na; + use mpc_algebra::honest_but_curious as hbc; use mpc_algebra::malicious_majority as mm; @@ -101,8 +104,8 @@ impl> ConstraintSynthesizer for KeyPublicizeCir // is_fortune_teller = 0 or 1 for b in is_ft_var.iter() { - let is_zero = FieldVar::::is_zero(b)?; - let is_one = FieldVar::::is_one(b)?; + let is_zero = ark_r1cs_std::prelude::FieldVar::::is_zero(b)?; + let is_one = ark_r1cs_std::prelude::FieldVar::::is_one(b)?; let is_bool = is_zero.or(&is_one)?; is_bool.enforce_equal(&Boolean::constant(true))?; } @@ -663,7 +666,10 @@ impl ConstraintSynthesizer> for AnonymousVotingCircuit> as MpcFieldVar< + mm::MpcField, + mm::MpcField, + >>::zero(); for j in 0..player_num { each_num_voted += is_target_id_var[j][i].clone(); @@ -719,6 +725,254 @@ impl ConstraintSynthesizer> for AnonymousVotingCircuit> { + // parameter + pub num_players: usize, + + // instance + pub tau_matrix: na::DMatrix, + pub result: Vec, // TODO: delete later + + // witness + pub shuffle_matrices: Vec>, +} + +impl ConstraintSynthesizer for RoleAssignmentCircuit { + fn generate_constraints(self, cs: ConstraintSystemRef) -> ark_relations::r1cs::Result<()> { + // initialize + let tau_matrix_var = na::DMatrix::from_iterator( + self.tau_matrix.nrows(), + self.tau_matrix.ncols(), + self.tau_matrix.iter().map(|b| { + FpVar::new_witness(cs.clone(), || Ok(b)) + .expect("tau matrix var is not allocated correctly") + }), + ); + + // TODO: delete later + let result_input_var = self + .result + .iter() + .map(|x| FpVar::new_input(cs.clone(), || Ok(x))) + .collect::, _>>()?; + + let shuffle_matrix_var = self + .shuffle_matrices + .iter() + .map(|mat| { + na::DMatrix::from_iterator( + mat.nrows(), + mat.ncols(), + mat.iter().map(|b| { + FpVar::new_witness(cs.clone(), || Ok(b)) + .expect("shuffle matrix var is not allocated correctly") + }), + ) + }) + .collect::>(); + + let inverse_shuffle_matrix_var = self + .shuffle_matrices + .iter() + .map(|mat| { + na::DMatrix::from_iterator( + mat.nrows(), + mat.ncols(), + mat.transpose().iter().map(|b| { + FpVar::new_witness(cs.clone(), || Ok(b)) + .expect("shuffle matrix var is not allocated correctly") + }), + ) + }) + .collect::>(); + + // each shuffle matrix is a permutation matrix and sub matrix is a identity matrix + shuffle_matrix_var + .iter() + .for_each(|matrix| enforce_permutation_matrix(matrix, self.num_players).unwrap()); + + // calculate + // M = Product of shuffle_matrix + let matrix_M_var = shuffle_matrix_var + .clone() + .iter() + .skip(1) + .fold(shuffle_matrix_var[1].clone(), |acc, x| acc * x); + + let inverse_matrix_M_var = inverse_shuffle_matrix_var + .clone() + .iter() + .skip(1) + .fold(inverse_shuffle_matrix_var[1].clone(), |acc, x| acc * x); + + // rho = M^-1 * tau * M + let rho_var = inverse_matrix_M_var * &tau_matrix_var * &matrix_M_var; + + let mut rho_sequence_var = Vec::with_capacity(self.num_players); + let mut current_rho = rho_var.clone(); + for _ in 0..self.num_players { + rho_sequence_var.push(current_rho.clone()); + current_rho *= rho_var.clone(); // rho^(i+1) = rho^i * rho + } + + // input_result is consistent with the calculated result + let length = self.tau_matrix.nrows(); + + // 1. gen one-hot vector + let unit_vecs = (0..self.num_players) + .map(|i| test_one_hot_vector(length, i, cs.clone())) + .collect::>(); + + // 2. calculate rho^i * unit_vec_j to value + let calced_vec = unit_vecs + .iter() + .map(|unit_vec_j| { + rho_sequence_var + .iter() + .map(|rho| { + let res_index = rho * unit_vec_j.clone(); + test_index_to_value(res_index, true).unwrap() + }) + .collect::>() + }) + .collect::>(); + + let calced_role = calced_vec + .iter() + .map(|val| test_max(val, false).unwrap()) + .collect::>(); + + for i in 0..self.num_players { + calced_role[i].enforce_equal(&result_input_var[i])?; + } + + // [ ]: commitment + + Ok(()) + } +} + +impl ConstraintSynthesizer> for RoleAssignmentCircuit> { + fn generate_constraints( + self, + cs: ConstraintSystemRef>, + ) -> ark_relations::r1cs::Result<()> { + // initialize + let tau_matrix_var = na::DMatrix::from_iterator( + self.tau_matrix.nrows(), + self.tau_matrix.ncols(), + self.tau_matrix.iter().map(|b| { + MpcFpVar::new_witness(cs.clone(), || Ok(b)) + .expect("tau matrix var is not allocated correctly") + }), + ); + + // TODO: delete later + let result_input_var = self + .result + .iter() + .map(|x| MpcFpVar::new_input(cs.clone(), || Ok(x))) + .collect::, _>>()?; + + let shuffle_matrix_var = self + .shuffle_matrices + .iter() + .map(|mat| { + na::DMatrix::from_iterator( + mat.nrows(), + mat.ncols(), + mat.iter().map(|b| { + MpcFpVar::new_witness(cs.clone(), || Ok(b)) + .expect("shuffle matrix var is not allocated correctly") + }), + ) + }) + .collect::>(); + + let inverse_shuffle_matrix_var = self + .shuffle_matrices + .iter() + .map(|mat| { + na::DMatrix::from_iterator( + mat.nrows(), + mat.ncols(), + mat.transpose().iter().map(|b| { + MpcFpVar::new_witness(cs.clone(), || Ok(b)) + .expect("shuffle matrix var is not allocated correctly") + }), + ) + }) + .collect::>(); + + // each shuffle matrix is a permutation matrix and sub matrix is a identity matrix + shuffle_matrix_var + .iter() + .for_each(|matrix| enforce_permutation_matrix_mpc(matrix, self.num_players).unwrap()); + + // calculate + // M = Product of shuffle_matrix + let matrix_M_var = shuffle_matrix_var + .clone() + .iter() + .skip(1) + .fold(shuffle_matrix_var[1].clone(), |acc, x| acc * x); + + let inverse_matrix_M_var = inverse_shuffle_matrix_var + .clone() + .iter() + .skip(1) + .fold(inverse_shuffle_matrix_var[1].clone(), |acc, x| acc * x); + + // rho = M^-1 * tau * M + let rho_var = inverse_matrix_M_var * &tau_matrix_var * &matrix_M_var; + + let mut rho_sequence_var = Vec::with_capacity(self.num_players); + let mut current_rho = rho_var.clone(); + for _ in 0..self.num_players { + rho_sequence_var.push(current_rho.clone()); + current_rho *= rho_var.clone(); // rho^(i+1) = rho^i * rho + } + + // input_result is consistent with the calculated result + let length = self.tau_matrix.nrows(); + + // 1. gen one-hot vector + let unit_vecs = (0..self.num_players) + .map(|i| test_one_hot_vector_mpc(length, i, cs.clone())) + .collect::>(); + + // 2. calculate rho^i * unit_vec_j to value + let calced_vec = unit_vecs + .iter() + .map(|unit_vec_j| { + rho_sequence_var + .iter() + .map(|rho| { + let res_index = rho * unit_vec_j.clone(); + test_index_to_value_mpc(res_index, true).unwrap() + }) + .collect::>() + }) + .collect::>(); + + let calced_role = calced_vec + .iter() + .map(|val| test_max_mpc(val, false).unwrap()) + .collect::>(); + + for i in 0..self.num_players { + calced_role[i].enforce_equal(&result_input_var[i])?; + } + + // [ ]: commitment + + println!("total number of constraints: {}", cs.num_constraints()); + + Ok(()) + } +} + pub trait ElGamalLocalOrMPC { type JubJub: ProjectiveCurve; @@ -949,3 +1203,243 @@ impl ElGamalLocalOrMPC> for mm::MpcField { a.enforce_equal(b) } } + +fn test_max( + a: &[FpVar], + should_enforce: bool, +) -> Result, SynthesisError> { + let cs = a[0].cs().clone(); + let max_var = FpVar::new_witness(cs, || { + let max = a.iter().map(|x| x.value().unwrap()).max().unwrap(); + Ok(max) + })?; + + if should_enforce { + // [ ]: implement correctly + a.iter().for_each(|x| { + max_var + .enforce_cmp(x, core::cmp::Ordering::Greater, true) + .unwrap() + }); + } + + Ok(max_var) +} + +fn test_max_mpc( + a: &[MpcFpVar], + should_enforce: bool, +) -> Result, SynthesisError> { + let cs = a[0].cs().clone(); + let max_var = MpcFpVar::new_witness(cs, || { + let max = a.iter().map(|x| x.value().unwrap()).max().unwrap(); + Ok(max) + })?; + + if should_enforce { + // [ ]: implement correctly + a.iter().for_each(|x| { + max_var + .enforce_cmp(x, core::cmp::Ordering::Greater, true) + .unwrap() + }); + } + + Ok(max_var) +} + +fn test_index_to_value( + a: na::DVector>, + should_enforce: bool, +) -> Result, SynthesisError> { + let cs = a[0].cs().clone(); + let value_var = FpVar::new_witness(cs.clone(), || { + let res = a + .iter() + .position(|x| x.value().unwrap().is_one()) + .expect("This index vector is not a one-hot vector"); + Ok(F::from(res as u64)) + })?; + + if should_enforce { + let stair_vector = na::DVector::from( + (0..a.len()) + .map(|i| FpVar::new_constant(cs.clone(), F::from(i as u64)).unwrap()) + .collect::>(), + ); + let ip = a.dot(&stair_vector); + + ip.enforce_equal(&value_var)?; + } + Ok(value_var) +} + +fn test_index_to_value_mpc( + a: na::DVector>, + should_enforce: bool, +) -> Result, SynthesisError> +where + ::Base: Zero, +{ + let cs = a[0].cs().clone(); + let value_var = MpcFpVar::new_witness(cs.clone(), || { + let res = a + .iter() + .position(|x| x.value().unwrap().is_one()) + .expect("This index vector is not a one-hot vector"); + Ok(F::from(res as u64)) + })?; + + if should_enforce { + let stair_vector = na::DVector::from( + (0..a.len()) + .map(|i| MpcFpVar::new_constant(cs.clone(), F::from(i as u64)).unwrap()) + .collect::>(), + ); + let ip = a.dot(&stair_vector); + + ip.enforce_equal(&value_var)?; + } + Ok(value_var) +} + +fn test_one_hot_vector( + length: usize, + index: usize, + cs: ConstraintSystemRef, +) -> na::DVector> { + assert!(index < length); + let mut res = na::DVector::>::zeros(length); + for i in 0..length { + if i == index { + res[i] = FpVar::new_constant(cs.clone(), F::one()).unwrap(); + } else { + res[i] = FpVar::new_constant(cs.clone(), F::zero()).unwrap(); + } + } + res +} + +fn test_one_hot_vector_mpc( + length: usize, + index: usize, + cs: ConstraintSystemRef, +) -> na::DVector> +where + ::Base: Zero, +{ + assert!(index < length); + let mut res = na::DVector::>::zeros(length); + for i in 0..length { + if i == index { + res[i] = MpcFpVar::new_constant(cs.clone(), F::one()).unwrap(); + } else { + res[i] = MpcFpVar::new_constant(cs.clone(), F::zero()).unwrap(); + } + } + res +} + +fn enforce_permutation_matrix( + matrix: &na::DMatrix>, + n: usize, +) -> Result<(), SynthesisError> { + let size = matrix.nrows(); + // (0,0) ~ (n-1,n-1) is arbitrary permutation matrix + + for i in 0..n { + let mut i_th_row_sum = FpVar::zero(); + let mut i_th_column_sum = FpVar::zero(); + + for j in 0..n { + // all check 0 or 1 -> row sum and column sum is 1 + let val = &matrix[(i, j)]; + + val.is_eq(&FpVar::zero()) + .unwrap() + .or(&val.is_eq(&FpVar::one()).unwrap()) + .unwrap() + .enforce_equal(&Boolean::TRUE)?; + + // row column is ambiguos + i_th_row_sum += val; + i_th_column_sum += &matrix[(j, i)]; + } + + i_th_row_sum.enforce_equal(&FpVar::one())?; + i_th_column_sum.enforce_equal(&FpVar::one())?; + } + + for i in 0..size { + for j in 0..size { + if i >= n || j >= n { + // (n~n+m-1, n~n+m-1) is identity matrix + if i == j { + let val = &matrix[(i, j)]; + val.enforce_equal(&FpVar::one())?; + } else { + // other is 0 + let val = &matrix[(i, j)]; + val.enforce_equal(&FpVar::zero())?; + } + } + } + } + + Ok(()) +} + +fn enforce_permutation_matrix_mpc< + F: PrimeField + Reveal + ark_ff::SquareRootField + mpc_algebra::EqualityZero, +>( + matrix: &na::DMatrix>, + n: usize, +) -> Result<(), SynthesisError> +where + ::Base: Zero, +{ + let size = matrix.nrows(); + // (0,0) ~ (n-1,n-1) is arbitrary permutation matrix + + for i in 0..n { + let mut i_th_row_sum = as Zero>::zero(); + let mut i_th_column_sum = as Zero>::zero(); + + for j in 0..n { + // all check 0 or 1 -> row sum and column sum is 1 + let val = &matrix[(i, j)]; + + ( as Zero>::zero() - val) + .is_zero() + .unwrap() + .or(&( as One>::one() - val).is_zero().unwrap()) + .unwrap() + .enforce_equal(&MpcBoolean::TRUE)?; + + // row column is ambiguos + i_th_row_sum += val; + i_th_column_sum += &matrix[(j, i)]; + } + + i_th_row_sum.enforce_equal(& as One>::one())?; + i_th_column_sum.enforce_equal(& as One>::one())?; + } + + for i in 0..size { + for j in 0..size { + if i >= n || j >= n { + // (n~n+m-1, n~n+m-1) is identity matrix + if i == j { + let val = &matrix[(i, j)]; + val.enforce_equal(& as One>::one())?; + } else { + // other is 0 + let val = &matrix[(i, j)]; + val.enforce_equal(& as Zero>::zero())?; + } + } + } + } + + Ok(()) +} From 463808448b32047bf4665c603f139f22f1895936 Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 26 Sep 2024 20:10:21 +0900 Subject: [PATCH 06/12] =?UTF-8?q?=F0=9F=90=9B=20Modify=20arkworks=20(and?= =?UTF-8?q?=20MPC)=20Field=20for=20using=20nalgebra=20crate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- arkworks/nonnative/src/reduce.rs | 2 +- arkworks/r1cs-std/src/fields/fp/mod.rs | 23 ++++++++++++++++++++-- mpc-algebra/src/r1cs_helper/mpc_fp.rs | 27 ++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/arkworks/nonnative/src/reduce.rs b/arkworks/nonnative/src/reduce.rs index a6e1c20b..05d97a8d 100644 --- a/arkworks/nonnative/src/reduce.rs +++ b/arkworks/nonnative/src/reduce.rs @@ -3,7 +3,7 @@ use crate::{overhead, AllocatedNonNativeFieldVar}; use ark_ff::{biginteger::BigInteger, fields::FpParameters, BitIteratorBE, One, PrimeField, Zero}; use ark_r1cs_std::eq::EqGadget; use ark_r1cs_std::fields::fp::FpVar; -use ark_r1cs_std::fields::FieldVar; +// use hark_r1cs_std::fields::FieldVar; use ark_r1cs_std::{alloc::AllocVar, boolean::Boolean, R1CSVar}; use ark_relations::{ ns, diff --git a/arkworks/r1cs-std/src/fields/fp/mod.rs b/arkworks/r1cs-std/src/fields/fp/mod.rs index df5b76c3..77e4bff9 100644 --- a/arkworks/r1cs-std/src/fields/fp/mod.rs +++ b/arkworks/r1cs-std/src/fields/fp/mod.rs @@ -15,7 +15,7 @@ mod cmp; /// Represents a variable in the constraint system whose /// value can be an arbitrary field element. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] #[must_use] pub struct AllocatedFp { pub(crate) value: Option, @@ -38,7 +38,7 @@ impl AllocatedFp { } /// Represent variables corresponding to a field element in `F`. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] #[must_use] pub enum FpVar { /// Represents a constant in the constraint system, which means that @@ -644,6 +644,25 @@ impl AllocVar for AllocatedFp { } } +impl ark_std::Zero for FpVar { + fn zero() -> Self { + Self::Constant(F::zero()) + } + + fn is_zero(&self) -> bool { + match self { + Self::Constant(c) => c.is_zero(), + Self::Var(v) => v.value.expect("value is None").is_zero(), + } + } +} + +impl ark_std::One for FpVar { + fn one() -> Self { + Self::Constant(F::one()) + } +} + impl FieldVar for FpVar { fn constant(f: F) -> Self { Self::Constant(f) diff --git a/mpc-algebra/src/r1cs_helper/mpc_fp.rs b/mpc-algebra/src/r1cs_helper/mpc_fp.rs index f70c3eaa..cb0cb713 100644 --- a/mpc-algebra/src/r1cs_helper/mpc_fp.rs +++ b/mpc-algebra/src/r1cs_helper/mpc_fp.rs @@ -25,7 +25,7 @@ use crate::{ /// Represents a variable in the constraint system whose /// value can be an arbitrary field element. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] #[must_use] pub struct MpcAllocatedFp { pub(crate) value: Option, @@ -48,7 +48,7 @@ impl MpcAllocatedFp { } /// Represent variables corresponding to a field element in `F`. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] #[must_use] pub enum MpcFpVar { /// Represents a constant in the constraint system, which means that @@ -715,6 +715,29 @@ impl AllocVar for MpcAllocatedFp { } } +// TODO: Consider security of this implementation +impl Zero for MpcFpVar +where + ::Base: Zero, +{ + fn zero() -> Self { + Self::Constant(F::zero()) + } + + fn is_zero(&self) -> bool { + match self { + Self::Constant(c) => c.reveal().is_zero(), + Self::Var(v) => v.value.expect("value is None").reveal().is_zero(), + } + } +} + +impl One for MpcFpVar { + fn one() -> Self { + Self::Constant(F::one()) + } +} + impl MpcFieldVar for MpcFpVar { From 8645c163c2862d5fe237b4b272cc88c8fb3b7510 Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 3 Oct 2024 16:44:32 +0900 Subject: [PATCH 07/12] =?UTF-8?q?=E2=9C=A8=20Add=20Pedersen=20Commitment?= =?UTF-8?q?=20constraints=20for=20role=20assignment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/bin_werewolf.rs | 61 ++++++++++++++++++++++++++++++++++++++-- src/circuits/werewolf.rs | 43 +++++++++++++--------------- 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/examples/bin_werewolf.rs b/examples/bin_werewolf.rs index 855f212f..d968d590 100644 --- a/examples/bin_werewolf.rs +++ b/examples/bin_werewolf.rs @@ -1,17 +1,23 @@ use ark_bls12_377::{Fr, FrParameters}; use ark_crypto_primitives::encryption::AsymmetricEncryptionScheme; use ark_ec::AffineCurve; +use ark_ff::BigInteger; use ark_ff::FpParameters; +use ark_ff::PrimeField; use ark_marlin::IndexProverKey; use ark_mnt4_753::FqParameters; use ark_serialize::{CanonicalDeserialize, Read}; use ark_std::test_rng; use ark_std::One; +use ark_std::PubUniformRand; +use ark_std::UniformRand; use ark_std::Zero; use core::num; use core::panic; use mpc_algebra::encryption::elgamal::elgamal::Parameters; use mpc_algebra::malicious_majority::*; +use mpc_algebra::CommitmentScheme; +use mpc_algebra::FromLocal; use mpc_algebra::Reveal; use mpc_net::{MpcMultiNet as Net, MpcNet}; use rand::seq::SliceRandom; @@ -21,6 +27,7 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::{fs::File, path::PathBuf}; use structopt::StructOpt; +use zk_mpc::circuits::LocalOrMPC; use zk_mpc::circuits::RoleAssignmentCircuit; use zk_mpc::circuits::{ AnonymousVotingCircuit, DivinationCircuit, ElGamalLocalOrMPC, KeyPublicizeCircuit, @@ -326,6 +333,8 @@ fn role_assignment(opt: &Opt) -> Result<(), std::io::Error> { let rng = &mut test_rng(); + let pedersen_param = >::PedersenComScheme::setup(rng).unwrap(); + // calc let shuffle_matrix = vec![ generate_individual_shuffle_matrix( @@ -348,23 +357,71 @@ fn role_assignment(opt: &Opt) -> Result<(), std::io::Error> { println!("inputs is {:?}", inputs); + let randomness = (0..n) + .map(|_| >::PedersenRandomness::rand(rng)) + .collect::>(); + + let commitment = (0..n) + .map(|i| { + >::PedersenComScheme::commit( + &pedersen_param, + &inputs[i].into_repr().to_bytes_le(), + &randomness[i], + ) + .unwrap() + }) + .collect::>(); + // prove let local_role_circuit = RoleAssignmentCircuit { num_players: n, + pedersen_param: pedersen_param.clone(), tau_matrix: na::DMatrix::::zeros(n + m, n + m), - result: inputs.clone(), shuffle_matrices: vec![na::DMatrix::::zeros(n + m, n + m); 2], + role_commitment: commitment, + randomness, }; let (mpc_index_pk, index_vk) = setup_and_index(local_role_circuit); + let mpc_pedersen_param = >::PedersenParam::from_local(&pedersen_param); + + let mpc_randomness = (0..n) + .map(|_| >::PedersenRandomness::rand(rng)) + .collect::>(); + + let converted_inputs = inputs + .iter() + .map(|x| >::convert_input(&MFr::from_public(*x))) + .collect::>(); + + let role_commitment = (0..n) + .map(|i| { + >::PedersenComScheme::commit( + &mpc_pedersen_param, + &converted_inputs[i], + &mpc_randomness[i], + ) + .unwrap() + }) + .collect::>(); + let mpc_role_circuit = RoleAssignmentCircuit { num_players: n, + pedersen_param: mpc_pedersen_param, tau_matrix: grouping_parameter.generate_tau_matrix(), - result: inputs.iter().map(|x| MFr::from_public(*x)).collect(), shuffle_matrices: shuffle_matrix, + randomness: mpc_randomness, + role_commitment: role_commitment.clone(), }; + let mut inputs = Vec::new(); + + role_commitment.iter().for_each(|x| { + inputs.push(x.reveal().x); + inputs.push(x.reveal().y); + }); + assert!(prove_and_verify( &mpc_index_pk, &index_vk, diff --git a/src/circuits/werewolf.rs b/src/circuits/werewolf.rs index c5468046..c87d0823 100644 --- a/src/circuits/werewolf.rs +++ b/src/circuits/werewolf.rs @@ -9,7 +9,6 @@ use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::boolean::Boolean; use ark_r1cs_std::eq::EqGadget; use ark_r1cs_std::fields::fp::FpVar; -use ark_r1cs_std::fields::FieldVar; use ark_r1cs_std::groups::CurveVar; use ark_r1cs_std::select::CondSelectGadget; use ark_r1cs_std::{R1CSVar, ToBitsGadget}; @@ -729,13 +728,15 @@ impl ConstraintSynthesizer> for AnonymousVotingCircuit> { // parameter pub num_players: usize, + pub pedersen_param: F::PedersenParam, // instance pub tau_matrix: na::DMatrix, - pub result: Vec, // TODO: delete later + pub role_commitment: Vec, // witness pub shuffle_matrices: Vec>, + pub randomness: Vec, } impl ConstraintSynthesizer for RoleAssignmentCircuit { @@ -750,13 +751,6 @@ impl ConstraintSynthesizer for RoleAssignmentCircuit { }), ); - // TODO: delete later - let result_input_var = self - .result - .iter() - .map(|x| FpVar::new_input(cs.clone(), || Ok(x))) - .collect::, _>>()?; - let shuffle_matrix_var = self .shuffle_matrices .iter() @@ -843,12 +837,17 @@ impl ConstraintSynthesizer for RoleAssignmentCircuit { .map(|val| test_max(val, false).unwrap()) .collect::>(); + // commitment for i in 0..self.num_players { - calced_role[i].enforce_equal(&result_input_var[i])?; + let pedersen_circuit = PedersenComCircuit { + param: Some(self.pedersen_param.clone()), + input: calced_role[i].value().unwrap_or_default(), + open: self.randomness[i].clone(), + commit: self.role_commitment[i], + }; + pedersen_circuit.generate_constraints(cs.clone())?; } - // [ ]: commitment - Ok(()) } } @@ -868,13 +867,6 @@ impl ConstraintSynthesizer> for RoleAssignmentCircuit, _>>()?; - let shuffle_matrix_var = self .shuffle_matrices .iter() @@ -961,12 +953,17 @@ impl ConstraintSynthesizer> for RoleAssignmentCircuit>(); + // commitment for i in 0..self.num_players { - calced_role[i].enforce_equal(&result_input_var[i])?; + let pedersen_circuit = PedersenComCircuit { + param: Some(self.pedersen_param.clone()), + input: calced_role[i].value().unwrap(), + open: self.randomness[i], + commit: self.role_commitment[i], + }; + pedersen_circuit.generate_constraints(cs.clone())?; } - // [ ]: commitment - println!("total number of constraints: {}", cs.num_constraints()); Ok(()) @@ -1215,7 +1212,7 @@ fn test_max( })?; if should_enforce { - // [ ]: implement correctly + // each element must be less than half of the modulus a.iter().for_each(|x| { max_var .enforce_cmp(x, core::cmp::Ordering::Greater, true) From 45092d704ff5de17324c4019f7ca4567f873112d Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 3 Oct 2024 16:46:11 +0900 Subject: [PATCH 08/12] =?UTF-8?q?=E2=9C=A8=20Support=20bitdecomposition=20?= =?UTF-8?q?for=20public=20MpcField?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mpc-algebra/src/wire/field.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mpc-algebra/src/wire/field.rs b/mpc-algebra/src/wire/field.rs index dc225f73..235623c6 100644 --- a/mpc-algebra/src/wire/field.rs +++ b/mpc-algebra/src/wire/field.rs @@ -716,7 +716,9 @@ impl> BitDecomposition for Mpc h[..l].to_vec() // remove the last element } false => { - panic!("public is not expected here"); + // This can be faster. + Self::king_share(self.unwrap_as_public(), &mut ark_std::test_rng()) + .bit_decomposition() } } } From 4a99cc24533425cb5def7cc94ba9965890c9c5ab Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 3 Oct 2024 16:49:08 +0900 Subject: [PATCH 09/12] =?UTF-8?q?=F0=9F=90=9B=20Update=20marlin=20srs=20pa?= =?UTF-8?q?rameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/marlin.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/marlin.rs b/src/marlin.rs index 0153f11d..82cadcd9 100644 --- a/src/marlin.rs +++ b/src/marlin.rs @@ -37,7 +37,8 @@ pub fn setup_and_index>( IndexVerifierKey, ) { let rng = &mut test_rng(); - let srs = LocalMarlin::universal_setup(10000, 50, 100, rng).unwrap(); + let srs = LocalMarlin::universal_setup(1000000, 50000, 100000, rng).unwrap(); + // let srs = LocalMarlin::universal_setup(30000, 500, 1000, rng).unwrap(); let (index_pk, index_vk) = LocalMarlin::index(&srs, circuit).unwrap(); let mpc_index_pk = IndexProverKey::from_public(index_pk); (mpc_index_pk, index_vk) From 3154e7ad5460f183aabf1b854569e58f6af4f68e Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 3 Oct 2024 17:03:48 +0900 Subject: [PATCH 10/12] =?UTF-8?q?=F0=9F=8E=A8=20Rabase=20srs=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/bin_werewolf.rs | 7 +++++-- src/marlin.rs | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/bin_werewolf.rs b/examples/bin_werewolf.rs index d968d590..ecdfb325 100644 --- a/examples/bin_werewolf.rs +++ b/examples/bin_werewolf.rs @@ -1,5 +1,6 @@ use ark_bls12_377::{Fr, FrParameters}; use ark_crypto_primitives::encryption::AsymmetricEncryptionScheme; +use ark_ec::twisted_edwards_extended::GroupAffine; use ark_ec::AffineCurve; use ark_ff::BigInteger; use ark_ff::FpParameters; @@ -382,7 +383,9 @@ fn role_assignment(opt: &Opt) -> Result<(), std::io::Error> { randomness, }; - let (mpc_index_pk, index_vk) = setup_and_index(local_role_circuit); + let srs = LocalMarlin::universal_setup(1000000, 50000, 100000, rng).unwrap(); + let (index_pk, index_vk) = LocalMarlin::index(&srs, local_role_circuit).unwrap(); + let mpc_index_pk = IndexProverKey::from_public(index_pk); let mpc_pedersen_param = >::PedersenParam::from_local(&pedersen_param); @@ -539,7 +542,7 @@ fn test_shuffle_matrix() { )]; for id in 0..grouping_parameter.get_num_players() { - let (role, player_ids) = + let (role, _, player_ids) = calc_shuffle_matrix(&grouping_parameter, &shuffle_matrix, id).unwrap(); println!("role is {:?}", role); println!("fellow is {:?}", player_ids); diff --git a/src/marlin.rs b/src/marlin.rs index 82cadcd9..75709f89 100644 --- a/src/marlin.rs +++ b/src/marlin.rs @@ -37,8 +37,7 @@ pub fn setup_and_index>( IndexVerifierKey, ) { let rng = &mut test_rng(); - let srs = LocalMarlin::universal_setup(1000000, 50000, 100000, rng).unwrap(); - // let srs = LocalMarlin::universal_setup(30000, 500, 1000, rng).unwrap(); + let srs = LocalMarlin::universal_setup(30000, 500, 1000, rng).unwrap(); let (index_pk, index_vk) = LocalMarlin::index(&srs, circuit).unwrap(); let mpc_index_pk = IndexProverKey::from_public(index_pk); (mpc_index_pk, index_vk) From 14bb6fe5ab13d143b8463370ba1af8ba68e672f7 Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 3 Oct 2024 17:37:52 +0900 Subject: [PATCH 11/12] =?UTF-8?q?=F0=9F=90=9B=20Resolve=20multiple=20zero?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/circuits/equality_zero.rs | 3 +- src/circuits/werewolf.rs | 79 +++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/circuits/equality_zero.rs b/src/circuits/equality_zero.rs index 90a1eeb1..8bf26f45 100644 --- a/src/circuits/equality_zero.rs +++ b/src/circuits/equality_zero.rs @@ -1,5 +1,4 @@ use ark_ff::PrimeField; -use ark_ff::Zero; use ark_r1cs_std::{ alloc::AllocVar, boolean::Boolean, @@ -59,7 +58,7 @@ impl ConstraintSynthesizer for NotEqualityZeroCircuit { fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { let a_var = FpVar::new_witness(cs.clone(), || Ok(self.a))?; - let is_zero_var = Boolean::new_input(cs.clone(), || Ok(self.a.is_zero()))?; + let is_zero_var = Boolean::new_input(cs.clone(), || Ok(ark_ff::Zero::is_zero(&self.a)))?; a_var.is_zero()?.enforce_equal(&is_zero_var)?; diff --git a/src/circuits/werewolf.rs b/src/circuits/werewolf.rs index 3631cd19..f2bc6e14 100644 --- a/src/circuits/werewolf.rs +++ b/src/circuits/werewolf.rs @@ -9,6 +9,7 @@ use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::boolean::Boolean; use ark_r1cs_std::eq::EqGadget; use ark_r1cs_std::fields::fp::FpVar; +use ark_r1cs_std::fields::FieldVar; use ark_r1cs_std::groups::CurveVar; use ark_r1cs_std::select::CondSelectGadget; use ark_r1cs_std::{R1CSVar, ToBitsGadget}; @@ -109,21 +110,23 @@ impl> ConstraintSynthesizer for KeyPublicizeCir is_bool.enforce_equal(&Boolean::constant(true))?; } - let _sum_x_var = x_var - .iter() - .enumerate() - .fold(FpVar::::zero(), |mut acc, (i, x)| { - acc = acc + x * &is_ft_var[i]; - acc - }); - - let _sum_y_var = y_var - .iter() - .enumerate() - .fold(FpVar::::zero(), |mut acc, (i, y)| { - acc = acc + y * &is_ft_var[i]; - acc - }); + let _sum_x_var = + x_var + .iter() + .enumerate() + .fold( as Zero>::zero(), |mut acc, (i, x)| { + acc = acc + x * &is_ft_var[i]; + acc + }); + + let _sum_y_var = + y_var + .iter() + .enumerate() + .fold( as Zero>::zero(), |mut acc, (i, y)| { + acc = acc + y * &is_ft_var[i]; + acc + }); // self.verify_commitments(cs.clone())?; @@ -597,7 +600,7 @@ impl ConstraintSynthesizer for AnonymousVotingCircuit { let mut num_voted_var = Vec::new(); for i in 0..player_num { - let mut each_num_voted = FpVar::zero(); + let mut each_num_voted = as Zero>::zero(); for j in 0..player_num { each_num_voted += is_target_id_var[j][i].clone(); @@ -748,15 +751,18 @@ impl ConstraintSynthesizer for WinningJudgeCircuit { let game_state_var = FpVar::new_input(cs.clone(), || Ok(self.game_state))?; // calculate - let num_werewolf_var = am_werewolf_var.iter().fold(FpVar::zero(), |mut acc, x| { - acc += x; - acc - }); + let num_werewolf_var = + am_werewolf_var + .iter() + .fold( as Zero>::zero(), |mut acc, x| { + acc += x; + acc + }); let num_citizen_var = num_alive_var - &num_werewolf_var; let calced_game_state_var = FpVar::conditionally_select( - &num_werewolf_var.is_zero()?, + &FieldVar::is_zero(&num_werewolf_var)?, &FpVar::constant(Fr::from(2)), // villager win &FpVar::conditionally_select( &num_werewolf_var.is_cmp(&num_citizen_var, std::cmp::Ordering::Less, false)?, @@ -804,10 +810,13 @@ impl ConstraintSynthesizer> for WinningJudgeCircuit> as Zero>::zero(), + |mut acc, x| { + acc += x; + acc + }, + ); let num_citizen_var = num_alive_var - &num_werewolf_var; @@ -841,7 +850,7 @@ impl ConstraintSynthesizer> for WinningJudgeCircuit> { // parameter @@ -969,7 +978,7 @@ impl ConstraintSynthesizer for RoleAssignmentCircuit { Ok(()) } } - + impl ConstraintSynthesizer> for RoleAssignmentCircuit> { fn generate_constraints( self, @@ -1463,16 +1472,16 @@ fn enforce_permutation_matrix( // (0,0) ~ (n-1,n-1) is arbitrary permutation matrix for i in 0..n { - let mut i_th_row_sum = FpVar::zero(); - let mut i_th_column_sum = FpVar::zero(); + let mut i_th_row_sum = as Zero>::zero(); + let mut i_th_column_sum = as Zero>::zero(); for j in 0..n { // all check 0 or 1 -> row sum and column sum is 1 let val = &matrix[(i, j)]; - val.is_eq(&FpVar::zero()) + val.is_eq(& as Zero>::zero()) .unwrap() - .or(&val.is_eq(&FpVar::one()).unwrap()) + .or(&val.is_eq(& as One>::one()).unwrap()) .unwrap() .enforce_equal(&Boolean::TRUE)?; @@ -1481,8 +1490,8 @@ fn enforce_permutation_matrix( i_th_column_sum += &matrix[(j, i)]; } - i_th_row_sum.enforce_equal(&FpVar::one())?; - i_th_column_sum.enforce_equal(&FpVar::one())?; + i_th_row_sum.enforce_equal(& as One>::one())?; + i_th_column_sum.enforce_equal(& as One>::one())?; } for i in 0..size { @@ -1491,11 +1500,11 @@ fn enforce_permutation_matrix( // (n~n+m-1, n~n+m-1) is identity matrix if i == j { let val = &matrix[(i, j)]; - val.enforce_equal(&FpVar::one())?; + val.enforce_equal(& as One>::one())?; } else { // other is 0 let val = &matrix[(i, j)]; - val.enforce_equal(&FpVar::zero())?; + val.enforce_equal(& as Zero>::zero())?; } } } From e1b4aa234a94cb7aced20b9679fa42e6ea3db3d1 Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 3 Oct 2024 17:46:11 +0900 Subject: [PATCH 12/12] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20cargo=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/bin_werewolf.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/bin_werewolf.rs b/examples/bin_werewolf.rs index cd6c6a89..ac63a496 100644 --- a/examples/bin_werewolf.rs +++ b/examples/bin_werewolf.rs @@ -32,7 +32,8 @@ use std::{fs::File, path::PathBuf}; use structopt::StructOpt; use zk_mpc::circuits::LocalOrMPC; use zk_mpc::circuits::{ - AnonymousVotingCircuit, DivinationCircuit, ElGamalLocalOrMPC, KeyPublicizeCircuit, RoleAssignmentCircuit, WinningJudgeCircuit + AnonymousVotingCircuit, DivinationCircuit, ElGamalLocalOrMPC, KeyPublicizeCircuit, + RoleAssignmentCircuit, WinningJudgeCircuit, }; use zk_mpc::input::InputWithCommit; use zk_mpc::input::MpcInputTrait;