use crate::pari_init;
use crate::primitives::hash_to_prime;
use crate::primitives::is_prime;
use crate::primitives::prng;
use crate::primitives::ErrorReason;
use crate::ABDeltaTriple;
use crate::BinaryQF;
use curv::arithmetic::traits::*;
use curv::BigInt;
pub struct VDF {
pub x: BigInt,
pub y: BinaryQF,
pub pi: BinaryQF,
pub t: BigInt,
a_b_delta: ABDeltaTriple,
}
impl VDF {
pub fn setup(security_param: usize, x: &BigInt) -> ABDeltaTriple {
let mut disc: BigInt;
disc = -BigInt::sample(security_param); while disc.mod_floor(&BigInt::from(4)) != BigInt::one() || !is_prime(&(-&disc)) {
disc = -BigInt::sample(security_param);
}
unsafe {
pari_init(1000000000, 2);
}
let (a, b) = h_g(&disc, x);
ABDeltaTriple { a, b, delta: disc }
}
pub fn eval(a_b_delta: &ABDeltaTriple, x: &BigInt, t: &BigInt) -> Self {
unsafe {
pari_init(1000000000, 2);
}
let g = BinaryQF::binary_quadratic_form_disc(a_b_delta).reduce();
let mut y = g.clone();
let mut i = BigInt::zero();
while &i < t {
y = y.compose(&y).reduce();
i += BigInt::one();
}
let l = hash_to_prime(&g, &y);
let mut i = BigInt::zero();
let mut b: BigInt;
let mut r = BigInt::one();
let mut r2: BigInt;
let two = BigInt::from(2);
let mut pi = BinaryQF::binary_quadratic_form_principal(&a_b_delta.delta);
while &i < t {
r2 = &r * &two;
b = r2.div_floor(&l);
r = r2.mod_floor(&l);
pi = pi.exp(&two).compose(&g.exp(&b)).reduce();
i += BigInt::one();
}
VDF {
x: x.clone(),
y,
pi,
t: t.clone(),
a_b_delta: a_b_delta.clone(),
}
}
pub fn verify(&self) -> Result<(), ErrorReason> {
unsafe {
pari_init(1000000000, 2);
}
let g = BinaryQF::binary_quadratic_form_disc(&self.a_b_delta).reduce();
if g.discriminant() != self.a_b_delta.delta
|| self.y.discriminant() != self.a_b_delta.delta
|| self.pi.discriminant() != self.a_b_delta.delta
{
return Err(ErrorReason::VDFVerifyError);
}
let l = hash_to_prime(&g, &self.y);
let r = BigInt::mod_pow(&BigInt::from(2), &self.t, &l);
let pi_l_g_r = self.pi.exp(&l).compose(&g.exp(&r)).reduce();
match pi_l_g_r == self.y {
true => Ok(()),
false => Err(ErrorReason::VDFVerifyError),
}
}
}
fn h_g(disc: &BigInt, x: &BigInt) -> (BigInt, BigInt) {
let mut i = 0;
let two = BigInt::from(2);
let max = BigInt::from(20);
let mut b = &two * prng(x, i, disc.bit_length()) + BigInt::one();
let mut c = two.clone();
let mut b2_minus_disc: BigInt = b.pow(2) - disc;
let four = BigInt::from(4);
let mut u = b2_minus_disc.div_floor(&four);
while u.mod_floor(&c) != BigInt::zero() {
b = &two * prng(x, i, disc.bit_length()) + BigInt::one();
b2_minus_disc = b.pow(2) - disc;
u = b2_minus_disc.div_floor(&four);
i += 1;
c = (&c.next_prime()).mod_floor(&max);
}
let a = u.div_floor(&c);
(a, b)
}
#[cfg(test)]
mod tests {
use super::VDF;
use crate::curv::arithmetic::traits::Samplable;
use curv::BigInt;
use std::time::Instant;
#[test]
fn test_vdf_valid_proof() {
let sec = 1600;
let t = BigInt::sample(10); let x = BigInt::from(10);
let a_b_delta = VDF::setup(sec, &x);
let mut i = 0;
while i < 10 {
let start = Instant::now();
let vdf_out_proof = VDF::eval(&a_b_delta, &x, &t);
let duration1 = start.elapsed();
let start = Instant::now();
let res = vdf_out_proof.verify();
let duration2 = start.elapsed();
i += 1;
println!("eval time: {:?}", duration1);
println!("verify time: {:?}", duration2);
assert!(res.is_ok());
}
}
#[test]
#[should_panic]
fn test_vdf_wrong_proof() {
let sec = 1600;
let t = BigInt::sample(10);
let x = BigInt::from(10);
let a_b_delta = VDF::setup(sec, &x);
let mut vdf_out_proof = VDF::eval(&a_b_delta, &x, &t);
println!("before: {:?}", vdf_out_proof.y);
vdf_out_proof.y = vdf_out_proof.y.exp(&BigInt::from(3));
println!("after: {:?}", vdf_out_proof.y);
let res = vdf_out_proof.verify();
assert!(res.is_ok());
}
}