Skip to content

Commit

Permalink
fix(optimizing unit vector zkp): updates - generation and verification
Browse files Browse the repository at this point in the history
  • Loading branch information
cong-or committed Jan 4, 2024
1 parent fa130d7 commit b5c4148
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 93 deletions.
34 changes: 30 additions & 4 deletions chain-vote/benches/shvzk.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use chain_vote::*;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use rand::Rng;
use rand_chacha::ChaCha20Rng;
use rand_core::SeedableRng;

Expand All @@ -23,12 +24,36 @@ fn encrypt_and_prove(c: &mut Criterion) {
let crs = Crs::from_hash(&[0u8; 32]);
let ek = common(&mut rng);

for &number_candidates in [2usize, 4, 8].iter() {
for &number_candidates in [2usize, 4, 8, 16, 32, 64, 128, 256, 512].iter() {
let parameter_string = format!("{} candidates", number_candidates);
group.bench_with_input(
BenchmarkId::new("Encrypt and Prove", parameter_string),
&number_candidates,
|b, &nr| b.iter(|| ek.encrypt_and_prove_vote(&mut rng, &crs, Vote::new(nr, 0))),
|b, &nr| {
b.iter(|| ek.encrypt_and_prove_vote(&mut rng, &crs, Vote::new(nr, 0)))
},
);
}

group.finish();
}

fn prove(c: &mut Criterion) {
let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
let mut group = c.benchmark_group("Prove encrypted vote");
let crs = Crs::from_hash(&[0u8; 32]);
let ek = common(&mut rng);

for &number_candidates in [2usize, 4, 8, 16, 32, 64, 128, 256, 512].iter() {
group.bench_with_input(
BenchmarkId::new("Prove with", format!("{} candidates", number_candidates)),
&{
let vote = Vote::new(number_candidates, rng.gen_range(0.. number_candidates));
(vote, ek.encrypt_vote(&mut rng, vote))
},
|b, (vote, (vote_enc, randomness))| {
b.iter(|| ek.prove_encrypted_vote(&mut rng, &crs, *vote, vote_enc, randomness))
},
);
}

Expand All @@ -41,7 +66,7 @@ fn verify(c: &mut Criterion) {
let crs = Crs::from_hash(&[0u8; 32]);
let ek = common(&mut rng);

for &number_candidates in [2usize, 4, 8].iter() {
for &number_candidates in [2usize, 4, 8, 16, 32, 64, 128, 256, 512].iter() {
let (vote, proof) =
ek.encrypt_and_prove_vote(&mut rng, &crs, Vote::new(number_candidates, 0));
let parameter_string = format!("{} candidates", number_candidates);
Expand All @@ -60,7 +85,8 @@ criterion_group!(
config = Criterion::default().sample_size(500);
targets =
encrypt_and_prove,
prove,
verify,
);

criterion_main!(shvzk);
criterion_main!(shvzk);
41 changes: 40 additions & 1 deletion chain-vote/src/committee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,42 @@ impl ElectionPublicKey {
);
(ciphertexts, proof)
}
//-------------------------------------------------------------------------------------
// The encrypt_vote and prove_encrypted_vote methods are not the part of the original ElectionPublicKey trait,
// they are added only for a more selective benchmarking in this crate
//-------------------------------------------------------------------------------------
pub fn encrypt_vote<R: RngCore + CryptoRng>(
&self,
rng: &mut R,
vote: Vote,
) -> (EncryptedVote, Vec<Scalar>) {
let encryption_randomness = vec![Scalar::random(rng); vote.len()];
let vote_enc = encryption_randomness
.iter()
.zip(vote.iter())
.map(|(r, v)| self.as_raw().encrypt_with_r(&Scalar::from(v), r))
.collect();
(vote_enc, encryption_randomness)
}

pub fn prove_encrypted_vote<R: RngCore + CryptoRng>(
&self,
rng: &mut R,
crs: &Crs,
vote: Vote,
vote_enc: &EncryptedVote,
encryption_randomness: &[Scalar]
) -> ProofOfCorrectVote {
ProofOfCorrectVote::generate(
rng,
crs,
&self.0,
&vote,
encryption_randomness,
vote_enc,
)
}
//-------------------------------------------------------------------------------------
/// Create an election public key from all the participants of this committee
pub fn from_participants(pks: &[MemberPublicKey]) -> Self {
let mut k = pks[0].0.pk.clone();
Expand Down Expand Up @@ -170,6 +205,10 @@ impl MemberState {
&self.sk
}

pub fn member_secret_key(&self) -> MemberSecretKey {
self.sk.clone()
}

pub fn public_key(&self) -> MemberPublicKey {
MemberPublicKey(PublicKey {
pk: self.apubs[0].clone(),
Expand Down Expand Up @@ -313,4 +352,4 @@ impl Bech32 for MemberCommunicationPublicKey {
fn to_bech32_str(&self) -> String {
to_bech32_from_bytes::<Self>(&self.to_bytes())
}
}
}
93 changes: 59 additions & 34 deletions chain-vote/src/cryptography/zkps/unit_vector/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
//! same notation defined in Figure 8

use crate::cryptography::CommitmentKey;
use crate::encrypted_vote::binrep;
use crate::math::polynomial::Polynomial;
use crate::{GroupElement, Scalar};
use rand_core::{CryptoRng, RngCore};

Expand Down Expand Up @@ -119,42 +117,69 @@ impl ResponseRandomness {
}

/// Generate the polynomials used in Step 5, of the proof generation in Figure 8.
/// Denoting unit-vector's size as N, this method takes 2(N - 2) polynomial multiplications
/// instead of N * (logN - 1) for the direct implementation
pub(crate) fn generate_polys(
ciphers_len: usize,
idx_binary_rep: &[bool],
bits: usize,
blinding_randomness_vec: &[BlindingRandomness],
) -> Vec<Polynomial> {
// Compute polynomials pj(x)
let polys = idx_binary_rep
.iter()
.zip(blinding_randomness_vec.iter())
.map(|(ix, abcd)| {
let z1 = Polynomial::new(bits).set2(abcd.beta.clone(), (*ix).into());
let z0 = Polynomial::new(bits).set2(abcd.beta.negate(), (!ix).into());
(z0, z1)
})
.collect::<Vec<_>>();

let mut pjs = Vec::new();
for i in 0..ciphers_len {
let j = binrep(i, bits as u32);

let mut acc = if j[0] {
polys[0].1.clone()
) -> Vec<Vec<Scalar>> {
// Multiplication of an arbitrary-degree polynomial on a degree-1 polynomial with a binary non-const term
// By being tailored for a given specific type of poly_deg1-multiplier it
// has better performance than naive polynomials multiplication:
// at most poly.len() - 1 additions and poly.len() multiplications instead of 2 * poly.len()
//
// NOTE: should be replaced with naive polynomial multiplication, if const-time (data-independent)
// multiplication complexity is needed
#[inline]
fn mul(poly: &[Scalar], poly_deg1: &(Scalar, bool)) -> Vec<Scalar> {
let mut result = poly.iter().map(|p|p * &poly_deg1.0).collect::<Vec<_>>();
if poly_deg1.1 == true {
for i in 0.. poly.len() - 1 {
result[i + 1] = &result[i + 1] + &poly[i];
}
result.push(poly.last().unwrap().clone());
}
result
}
// Binary tree which leaves are the polynomials corresponding to the indices in range [0, bits)
fn polynomials_bin_tree(parent: &[Scalar], current_level: usize, params: &TreeParams) -> Vec<Vec<Scalar>> {
if current_level != params.max_level {
let next_level = current_level + 1;
let left_subtree = polynomials_bin_tree(
&mul(&parent, &params.deltas_0[current_level]),
next_level,
params
);
let right_subtree = polynomials_bin_tree(
&mul(&parent, &params.deltas_1[current_level]),
next_level,
params
);
left_subtree.into_iter().chain(right_subtree.into_iter()).collect()
} else {
polys[0].0.clone()
};
for k in 1..bits {
let t = if j[k] {
polys[k].1.clone()
} else {
polys[k].0.clone()
};
acc = acc * t;
vec![parent.to_vec()]
}
pjs.push(acc)
}

pjs
}
// Precomputed degree-1 polynomials with values of the Kronecker delta function (for both possible inputs: 0 and 1)
// and with corresponding beta-randomness for each bit of a given unit vector
let deltas_0 = (0.. bits).map(|i| {
(blinding_randomness_vec[i].beta.clone().negate(), !idx_binary_rep[i])
}).collect::<Vec<_>>();

let deltas_1 = (0.. bits).map(|i| {
(blinding_randomness_vec[i].beta.clone(), idx_binary_rep[i])
}).collect::<Vec<_>>();

struct TreeParams { max_level: usize, deltas_0: Vec<(Scalar, bool)>, deltas_1: Vec<(Scalar, bool)> }
let tp = TreeParams{ max_level: bits, deltas_0, deltas_1 };
// Building 2 subtrees from delta_0[0] and delta_1[0] to avoid 2 excessive multiplications with 1 as it would be with
// polynomials_bin_tree(&[Scalar::one()], 0, &tp)
let left_subtree = polynomials_bin_tree(
&[tp.deltas_0[0].0.clone(), Scalar::from(tp.deltas_0[0].1)], 1, &tp
);
let right_subtree = polynomials_bin_tree(
&[tp.deltas_1[0].0.clone(), Scalar::from(tp.deltas_1[0].1)], 1, &tp
);
left_subtree.into_iter().chain(right_subtree.into_iter()).collect()
}
Loading

0 comments on commit b5c4148

Please sign in to comment.