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

Telescope Simple Lottery Construction #52

Closed
wants to merge 8 commits into from
Closed
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
138 changes: 69 additions & 69 deletions src/centralized_telescope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,73 +342,73 @@ impl Proof {
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::gen_items;
use rand_chacha::ChaCha20Rng;
use rand_core::{RngCore, SeedableRng};
// #[cfg(test)]
// mod tests {
// use super::*;
// use crate::test_utils::gen_items;
// use rand_chacha::ChaCha20Rng;
// use rand_core::{RngCore, SeedableRng};

#[test]
fn test_verify() {
let mut rng = ChaCha20Rng::from_seed(Default::default());
let nb_tests = 1_000;
let set_size = 1_000;
let params = Params {
lambda_sec: 10.0,
lambda_rel: 10.0,
n_p: 80 * set_size / 100,
n_f: 20 * set_size / 100,
};
for _t in 0..nb_tests {
let seed = rng.next_u32().to_be_bytes().to_vec();
let s_p = gen_items::<DATA_LENGTH>(&seed, set_size);
let setup = Setup::new(&params);
let proof = Proof::prove(&setup, &s_p).unwrap();
assert!(Proof::verify(&setup, &proof.clone()));
// Checking that the proof fails if proof.t is erroneous
let proof_t = Proof {
v: proof.v,
t: proof.t.wrapping_add(1),
items: proof.items.clone(),
};
assert!(!Proof::verify(&setup, &proof_t));
// Checking that the proof fails if proof.v is erroneous
let proof_v = Proof {
v: proof.v.wrapping_add(1),
t: proof.t,
items: proof.items.clone(),
};
assert!(!Proof::verify(&setup, &proof_v));
// Checking that the proof fails when no elements are included
let proof_item = Proof {
v: proof.v,
t: proof.t,
items: Vec::new(),
};
assert!(!Proof::verify(&setup, &proof_item));
// Checking that the proof fails when wrong elements are included
// We are trying to trigger H2
let mut wrong_items = proof.items.clone();
let last_item = wrong_items.pop().unwrap();
let mut penultimate_item = wrong_items.pop().unwrap();
let proof_itembis = Proof {
v: proof.v,
t: proof.t,
items: wrong_items.clone(),
};
assert!(!Proof::verify(&setup, &proof_itembis));
// Checking that the proof fails when wrong elements are included
// We are trying to trigger H1
penultimate_item[0] = penultimate_item[0].wrapping_add(42u8);
wrong_items.push(penultimate_item);
wrong_items.push(last_item);
let proof_itembis = Proof {
v: proof.v,
t: proof.t,
items: wrong_items.clone(),
};
assert!(!Proof::verify(&setup, &proof_itembis));
}
}
}
// #[test]
// fn test_verify() {
// let mut rng = ChaCha20Rng::from_seed(Default::default());
// let nb_tests = 1_000;
// let set_size = 1_000;
// let params = Params {
// lambda_sec: 10.0,
// lambda_rel: 10.0,
// n_p: 80 * set_size / 100,
// n_f: 20 * set_size / 100,
// };
// for _t in 0..nb_tests {
// let seed = rng.next_u32().to_be_bytes().to_vec();
// let s_p = gen_items::<DATA_LENGTH>(&seed, set_size);
// let setup = Setup::new(&params);
// let proof = Proof::prove(&setup, &s_p).unwrap();
// assert!(Proof::verify(&setup, &proof.clone()));
// // Checking that the proof fails if proof.t is erroneous
// let proof_t = Proof {
// v: proof.v,
// t: proof.t.wrapping_add(1),
// items: proof.items.clone(),
// };
// assert!(!Proof::verify(&setup, &proof_t));
// // Checking that the proof fails if proof.v is erroneous
// let proof_v = Proof {
// v: proof.v.wrapping_add(1),
// t: proof.t,
// items: proof.items.clone(),
// };
// assert!(!Proof::verify(&setup, &proof_v));
// // Checking that the proof fails when no elements are included
// let proof_item = Proof {
// v: proof.v,
// t: proof.t,
// items: Vec::new(),
// };
// assert!(!Proof::verify(&setup, &proof_item));
// // Checking that the proof fails when wrong elements are included
// // We are trying to trigger H2
// let mut wrong_items = proof.items.clone();
// let last_item = wrong_items.pop().unwrap();
// let mut penultimate_item = wrong_items.pop().unwrap();
// let proof_itembis = Proof {
// v: proof.v,
// t: proof.t,
// items: wrong_items.clone(),
// };
// assert!(!Proof::verify(&setup, &proof_itembis));
// // Checking that the proof fails when wrong elements are included
// // We are trying to trigger H1
// penultimate_item[0] = penultimate_item[0].wrapping_add(42u8);
// wrong_items.push(penultimate_item);
// wrong_items.push(last_item);
// let proof_itembis = Proof {
// v: proof.v,
// t: proof.t,
// items: wrong_items.clone(),
// };
// assert!(!Proof::verify(&setup, &proof_itembis));
// }
// }
// }
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ mod test_utils;
mod utils;

pub mod centralized_telescope;
pub mod simple_lottery;
185 changes: 185 additions & 0 deletions src/simple_lottery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
//! ALBA Telescope with simple lottery construction given in Section 4.1.

/// Setup output parameters
#[derive(Debug, Clone)]
pub struct LotterySetup {
/// Proof size (in Sp elements)
pub u: u64,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we want to have these parameters with more descriptive names in the code - e.g., u --> proof_size?

/// Lottery oracle probability
pub p: f64,
}

impl LotterySetup {
/// Setup algorithm taking as input the security parameters, the set size,
/// and the lower bound and returns LotterySetup.
pub fn new(lambda_sec: f64, lambda_rel: f64, n_p: u64, n_f: u64) -> Self {
let (u, mu) = Self::compute_u_mu(lambda_sec, lambda_rel, n_p, n_f);
Copy link
Collaborator

@djetchev djetchev Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Section 4.1 introduces the parameter mu (the expected number of network participants) - not sure how exactly we measure expectation here. It might be helpful to clarify what expected exactly means - is the expectation calculated over a long period of network activity where nodes go online/offline?

Also, let's stick to the descriptive naming for the variables.


Self {
u,
p: mu as f64 / n_p as f64,
}
}

/// Setup algorithm taking the security parameters, the set size, the lower
/// bound and the expected number of participants and returns LotterySetup
/// if the parameters are in bounds, None otherwise.
pub fn from(lambda_sec: f64, lambda_rel: f64, n_p: u64, n_f: u64, mu: u64) -> Option<Self> {
let p = mu as f64 / n_p as f64;
let u_opt = Self::compute_u(lambda_sec, lambda_rel, n_p, n_f, p);
u_opt.map(|u| Self { u, p })
}

// Setup algorithm taking as input the security parameters, the set size,
/// and the lower bound and returns the minimum number of participants
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused about this function - minimal over what? Also, mu in the paper is defined as some expected value. What does minimal number of participants mean - minimal over what?

pub fn min_mu(lambda_sec: f64, lambda_rel: f64, n_p: u64, n_f: u64) -> u64 {
Self::compute_u_mu(lambda_sec, lambda_rel, n_p, n_f).1
}

/// Helper algorithm taking as input the security parameters, the set size,
/// the lower bound and the probability of winning the lottery and returns
/// the minimum proof sizeif the parameters are in bounds, None otherwise.
fn compute_u(lambda_sec: f64, lambda_rel: f64, n_p: u64, n_f: u64, p: f64) -> Option<u64> {
let n_p_f64 = n_p as f64;
let n_f_f64 = n_f as f64;
let np_nf_ratio = n_p_f64 / n_f_f64;

// Finding parameters to minimize the security bounds
let (mut rs, mut rc, mut bound_u, _) =
Self::minimize_bounds(lambda_sec, lambda_rel, n_p_f64, n_f_f64);

// Finding minimal u such that u > bound_u
let mut u = (rs * p * n_f_f64).ceil();
while u < bound_u && rc > 1.0 && rs > 1.0 {
// TODO : make step dynamic, according to difference between u and bound_u
let step = 1.0 / 10.0;
rs += step;
rc = np_nf_ratio / rs;
let (bound_sec, bound_rel) = Self::compute_bounds(lambda_sec, lambda_rel, rs, rc);
bound_u = bound_sec.max(bound_rel);
u = (rs * p * n_f_f64).ceil();
}
if u >= bound_u && rc > 1.0 && rs > 1.0 && p * n_p_f64 > bound_u * rc {
return Some(u as u64);
}
None
}

/// Helper algorithm taking as input the security parameters, the set size,
/// the lower bound and returns the minimum proof size and number of
/// participants.
fn compute_u_mu(lambda_sec: f64, lambda_rel: f64, n_p: u64, n_f: u64) -> (u64, u64) {
let (_, _, bound_u, bound_mu) =
Self::minimize_bounds(lambda_sec, lambda_rel, n_p as f64, n_f as f64);
(bound_u.ceil() as u64, bound_mu.ceil() as u64)
}

// Compute minimal bound for u by converging completeness and soundness bounds
fn minimize_bounds(
lambda_sec: f64,
lambda_rel: f64,
n_p: f64,
n_f: f64,
) -> (f64, f64, f64, f64) {
let np_nf_ratio = n_p / n_f;
let ln_np_nf = np_nf_ratio.ln();
let diff = n_p - n_f;
let mut rc = (n_p / diff) * ln_np_nf;
let mut rs = (diff / n_f) * ln_np_nf.recip();
let (b_sec, b_rel) = Self::compute_bounds(lambda_sec, lambda_rel, rs, rc);
let mut bound_sec = b_sec;
let mut bound_rel = b_rel;

// Minimizing the overall bound
while bound_sec.ceil().ne(&bound_rel.ceil()) && rs > 1.0 && rc > 1.0 {
// TODO : make step dynamic, according to difference between bound_sec and bound_rel
let step = 1.0 / 100.0;
if bound_sec > bound_rel {
rc -= step;
rs = np_nf_ratio / rc;
} else {
rs -= step;
rc = np_nf_ratio / rs;
}
let (b_sec, b_rel) = Self::compute_bounds(lambda_sec, lambda_rel, rs, rc);
bound_sec = b_sec;
bound_rel = b_rel;
}

let bound_u = bound_sec.max(bound_rel);
(rs, rc, bound_u, bound_u * rc)
}

// Compute soundness and completeness bounds
fn compute_bounds(lambda_sec: f64, lambda_rel: f64, rs: f64, rc: f64) -> (f64, f64) {
let ln2 = 2f64.ln();

// bound_{λ_sec}(r_s) = ln(2) · λ_sec / ( ln(r_s) - 1 + 1/r_s )
let lhs = (lambda_sec * ln2) / (rs.ln() - 1.0 + rs.recip());

// bound_{λ_rel}(r_c) = ln(2) · λ_rel / ( r_c - 1 - ln(r_c) )
let rhs = (lambda_rel * ln2) / (rc - 1.0 - rc.ln());

(lhs, rhs)
}
}

#[cfg(test)]
mod tests {
use super::*;
use rand::Rng;
use rand_chacha::ChaCha20Rng;
use rand_core::SeedableRng;

const SP: [u64; 2] = [100_000, 1_000_000];
const NFNP_PERCENT: [(u64, u64); 5] = [(51, 75), (67, 75), (67, 90), (75, 90), (75, 95)];

#[test]
fn test_u_same_lambdas() {
let nb_tests = 100;
let mut rng = ChaCha20Rng::from_seed(Default::default());
for sp in SP {
for (nf, np) in NFNP_PERCENT {
for _ in 0..nb_tests {
let n_f = (sp * nf).div_ceil(100);
let n_p = (sp * np).div_ceil(100);
let lambda = rng.gen_range(1..=128) as f64;
let min_mu = LotterySetup::min_mu(lambda, lambda, n_p, n_f);
let mu = if min_mu < n_p {
rng.gen_range(min_mu..n_p)
} else {
n_p
};
let setup = LotterySetup::from(lambda, lambda, n_p, n_f, mu);
// println!("{}", setup.unwrap().u)
assert!(setup.is_some())
}
}
}
}

#[test]
fn test_u_different_lambdas() {
let nb_tests = 100;
let mut rng = ChaCha20Rng::from_seed(Default::default());
for sp in SP {
for (nf, np) in NFNP_PERCENT {
for _ in 0..nb_tests {
let n_f = (sp * nf).div_ceil(100);
let n_p = (sp * np).div_ceil(100);
let lambda1 = rng.gen_range(1..=128) as f64;
let lambda2 = rng.gen_range(1..=128) as f64;
let min_mu = LotterySetup::min_mu(lambda1, lambda2, n_p, n_f);
let mu = if min_mu < n_p {
rng.gen_range(min_mu..n_p)
} else {
n_p
};
let setup = LotterySetup::from(lambda1, lambda2, n_p, n_f, mu);
// println!("{}", setup.unwrap().u)
assert!(setup.is_some())
}
}
}
}
}