Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/anonymous voting #57

Merged
merged 3 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/rust_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,6 @@ jobs:

- name: Run werewolf night
run: ./run_werewolf.zsh night

- name: Run werewolf vote
run: ./run_werewolf.zsh vote
87 changes: 86 additions & 1 deletion examples/bin_werewolf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ use mpc_net::{MpcMultiNet as Net, MpcNet};
use serde::Deserialize;
use std::{fs::File, path::PathBuf};
use structopt::StructOpt;
use zk_mpc::circuits::{DivinationCircuit, ElGamalLocalOrMPC, KeyPublicizeCircuit};
use zk_mpc::circuits::{
AnonymousVotingCircuit, DivinationCircuit, ElGamalLocalOrMPC, KeyPublicizeCircuit,
};
use zk_mpc::input::MpcInputTrait;
use zk_mpc::input::WerewolfKeyInput;
use zk_mpc::input::WerewolfMpcInput;
use zk_mpc::marlin::LocalMarlin;
use zk_mpc::marlin::MFr;
use zk_mpc::marlin::MpcMarlin;
use zk_mpc::marlin::{prove_and_verify, setup_and_index};
use zk_mpc::preprocessing;
use zk_mpc::serialize::{write_r, write_to_file};
use zk_mpc::she;
Expand All @@ -46,6 +49,16 @@ struct Opt {
input: Option<PathBuf>,
}

struct VoteArg {
target_id: usize,
}

impl VoteArg {
fn new(target_id: usize) -> Self {
Self { target_id }
}
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
let opt = Opt::from_args();

Expand All @@ -72,6 +85,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// run the night phase
night_werewolf(&opt)?;
}
"vote" => {
println!("Vote mode");
// run the vote phase
voting(&opt)?;
}
_ => {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
Expand Down Expand Up @@ -503,6 +521,73 @@ fn multi_divination(_opt: &Opt) -> Result<(), std::io::Error> {
Ok(())
}

fn voting(opt: &Opt) -> Result<(), std::io::Error> {
// init
Net::init_from_file(
opt.input.clone().unwrap().to_str().unwrap(),
opt.id.unwrap(),
);
// calc
let most_voted_id = Fr::from(1);
let invalid_most_voted_id = Fr::from(0);

// prove
let local_voting_circuit = AnonymousVotingCircuit {
is_target_id: vec![
vec![Fr::from(0), Fr::from(1), Fr::from(0)],
vec![Fr::from(0), Fr::from(1), Fr::from(0)],
vec![Fr::from(0), Fr::from(0), Fr::from(1)],
],
is_most_voted_id: Fr::from(1),
};

let (mpc_index_pk, index_vk) = setup_and_index(local_voting_circuit);

let rng = &mut test_rng();

let mpc_voting_circuit = AnonymousVotingCircuit {
is_target_id: vec![
vec![
MFr::king_share(Fr::from(0), rng),
MFr::king_share(Fr::from(1), rng),
MFr::king_share(Fr::from(0), rng),
],
vec![
MFr::king_share(Fr::from(0), rng),
MFr::king_share(Fr::from(1), rng),
MFr::king_share(Fr::from(0), rng),
],
vec![
MFr::king_share(Fr::from(0), rng),
MFr::king_share(Fr::from(0), rng),
MFr::king_share(Fr::from(1), rng),
],
],
is_most_voted_id: MFr::king_share(Fr::from(1), rng),
};

let inputs = vec![most_voted_id];
let invalid_inputs = vec![invalid_most_voted_id];

assert!(prove_and_verify(
&mpc_index_pk,
&index_vk,
mpc_voting_circuit.clone(),
inputs
));

assert!(!prove_and_verify(
&mpc_index_pk,
&index_vk,
mpc_voting_circuit,
invalid_inputs
));

println!("Player {} received the most votes", most_voted_id);

Ok(())
}

#[derive(Debug, Deserialize)]
struct ElGamalPubKey {
// public_key: GroupAffine<ark_ed_on_bls12_377::EdwardsParameters>,
Expand Down
2 changes: 1 addition & 1 deletion mpc-algebra/src/r1cs_helper/mpc_fp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl<F: PrimeField> MpcAllocatedFp<F> {
pub fn from(other: MpcBoolean<F>) -> Self {
let cs = other.cs();
let variable = cs.new_lc(other.lc()).unwrap();
Self::new(other.value().ok().map(|b| F::from(b as u8)), variable, cs)
Self::new(other.value_field().ok(), variable, cs)
}

/// Returns the value assigned to `self` in the underlying constraint system
Expand Down
17 changes: 17 additions & 0 deletions run_werewolf.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ night)
fi
done

for pid in ${PROCS[@]}; do
wait $pid
done
;;
vote)
for i in $(seq 0 $((players - 1))); do
if [ $i == 0 ]; then
RUST_BACKTRACE=1 $BIN vote $i ./data/address &
pid=$!
PROCS[$i]=$pid
else
$BIN vote $i ./data/address >/dev/null &
pid=$!
PROCS[$i]=$pid
fi
done

for pid in ${PROCS[@]}; do
wait $pid
done
Expand Down
161 changes: 158 additions & 3 deletions src/circuits/werewolf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ 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::ToBitsGadget;
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
use ark_std::One;
use ark_std::Zero;
use ark_std::{test_rng, One, Zero};
use mpc_algebra::groups::MpcCurveVar;
use mpc_algebra::{MpcBoolean, MpcEqGadget, Reveal};
use mpc_algebra::mpc_fields::MpcFieldVar;
use mpc_algebra::{
MpcBoolean, MpcCondSelectGadget, MpcEqGadget, MpcFpVar, MpcToBitsGadget, Reveal,
};

use mpc_algebra::honest_but_curious as hbc;
use mpc_algebra::malicious_majority as mm;
Expand Down Expand Up @@ -564,6 +568,157 @@ impl ConstraintSynthesizer<mm::MpcField<Fr>> for DivinationCircuit<mm::MpcField<
}
}

#[derive(Clone)]
pub struct AnonymousVotingCircuit<F: PrimeField + LocalOrMPC<F>> {
pub is_target_id: Vec<Vec<F>>,
pub is_most_voted_id: F,
}

impl ConstraintSynthesizer<Fr> for AnonymousVotingCircuit<Fr> {
fn generate_constraints(self, cs: ConstraintSystemRef<Fr>) -> ark_relations::r1cs::Result<()> {
// initialize
let player_num = self.is_target_id.len();

let is_target_id_var = self
.is_target_id
.iter()
.map(|id| {
id.iter()
.map(|b| FpVar::new_witness(cs.clone(), || Ok(b)))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?;

let is_most_voted_id_var = FpVar::new_input(cs.clone(), || Ok(self.is_most_voted_id))?;

// calculate
let mut num_voted_var = Vec::new();

for i in 0..player_num {
let mut each_num_voted = FpVar::zero();

for j in 0..player_num {
each_num_voted += is_target_id_var[j][i].clone();
}

num_voted_var.push(each_num_voted);
}

let constant = (0..4)
.map(|i| FpVar::Constant(Fr::from(i as i32)))
.collect::<Vec<_>>();

let mut calced_is_most_voted_id = FpVar::new_witness(cs.clone(), || Ok(Fr::zero()))?;

for i in 0..player_num {
let a_now = FpVar::conditionally_select_power_of_two_vector(
&calced_is_most_voted_id.to_bits_le().unwrap()[..2],
&constant,
)?;

let res = FpVar::is_cmp(
//&num_voted_var[calced_is_most_voted_id],
&a_now,
&num_voted_var[i],
std::cmp::Ordering::Greater,
true,
)?;

let false_value = FpVar::new_witness(cs.clone(), || Ok(Fr::from(i as i32)))?;

calced_is_most_voted_id =
FpVar::conditionally_select(&res, &calced_is_most_voted_id, &false_value)?;
}

// enforce equal
is_most_voted_id_var.enforce_equal(&calced_is_most_voted_id);

println!("total number of constraints: {}", cs.num_constraints());

Ok(())
}
}

impl ConstraintSynthesizer<mm::MpcField<Fr>> for AnonymousVotingCircuit<mm::MpcField<Fr>> {
fn generate_constraints(
self,
cs: ConstraintSystemRef<mm::MpcField<Fr>>,
) -> ark_relations::r1cs::Result<()> {
// initialize
let player_num = self.is_target_id.len();

let is_target_id_var = self
.is_target_id
.iter()
.map(|id| {
id.iter()
.map(|b| MpcFpVar::new_witness(cs.clone(), || Ok(b)))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?;

let is_most_voted_id_var = MpcFpVar::new_input(cs.clone(), || Ok(self.is_most_voted_id))?;

// calculate
let mut num_voted_var = Vec::new();

for i in 0..player_num {
let mut each_num_voted = MpcFpVar::zero();

for j in 0..player_num {
each_num_voted += is_target_id_var[j][i].clone();
}

num_voted_var.push(each_num_voted);
}

let constant = (0..4)
.map(|i| {
MpcFpVar::Constant(mm::MpcField::<Fr>::king_share(
Fr::from(i as i32),
&mut test_rng(),
))
})
.collect::<Vec<_>>();

let mut calced_is_most_voted_id = MpcFpVar::new_witness(cs.clone(), || {
Ok(mm::MpcField::<Fr>::king_share(Fr::zero(), &mut test_rng()))
})?;

for i in 0..player_num {
let a_now = MpcFpVar::conditionally_select_power_of_two_vector(
&calced_is_most_voted_id.to_bits_le().unwrap()[..2],
&constant,
)?;

let res = MpcFpVar::is_cmp(
//&num_voted_var[calced_is_most_voted_id],
&a_now,
&num_voted_var[i],
std::cmp::Ordering::Greater,
true,
)?;

let false_value = MpcFpVar::new_witness(cs.clone(), || {
Ok(mm::MpcField::<Fr>::king_share(
Fr::from(i as i32),
&mut test_rng(),
))
})?;

calced_is_most_voted_id =
MpcFpVar::conditionally_select(&res, &calced_is_most_voted_id, &false_value)?;
}

// enforce equal
is_most_voted_id_var.enforce_equal(&calced_is_most_voted_id);

println!("total number of constraints: {}", cs.num_constraints());

Ok(())
}
}

pub trait ElGamalLocalOrMPC<ConstraintF: PrimeField> {
type JubJub: ProjectiveCurve;

Expand Down
4 changes: 2 additions & 2 deletions src/marlin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
input::{MpcInputTrait, SampleMpcInput},
};

fn setup_and_index<C: ConstraintSynthesizer<Fr>>(
pub fn setup_and_index<C: ConstraintSynthesizer<Fr>>(
circuit: C,
) -> (
IndexProverKey<MFr, MpcMarlinKZG10>,
Expand All @@ -39,7 +39,7 @@ fn setup_and_index<C: ConstraintSynthesizer<Fr>>(
(mpc_index_pk, index_vk)
}

fn prove_and_verify<C: ConstraintSynthesizer<MFr>>(
pub fn prove_and_verify<C: ConstraintSynthesizer<MFr>>(
mpc_index_pk: &IndexProverKey<MFr, MpcMarlinKZG10>,
index_vk: &IndexVerifierKey<Fr, LocalMarlinKZG10>,
circuit: C,
Expand Down
Loading