use num_bigint::{BigUint, RandBigInt};
use num_traits::{One, Zero};
use rand_core::OsRng;
use sha2::{Digest, Sha256};
#[derive(Debug, Clone)]
pub struct VdfParams {
pub n: BigUint,
pub t: u64,
}
#[derive(Debug, Clone)]
pub struct VdfOutput {
pub y: BigUint,
pub proof: BigUint,
}
pub fn setup(t: u64, prime_bits: u32) -> VdfParams {
let p = generate_prime(prime_bits);
let q = generate_prime(prime_bits);
let n = &p * &q;
VdfParams { n, t }
}
pub fn eval(params: &VdfParams, x: &BigUint) -> VdfOutput {
let mut y = x.clone();
for _ in 0..params.t {
y = (&y * &y) % ¶ms.n;
}
let l = hash_to_prime(&y, x);
let exponent = BigUint::one() << params.t;
let quotient = &exponent / &l;
let proof = x.modpow("ient, ¶ms.n);
VdfOutput { y, proof }
}
pub fn verify(params: &VdfParams, x: &BigUint, output: &VdfOutput) -> bool {
let l = hash_to_prime(&output.y, x);
let y_l = output.y.modpow(&l, ¶ms.n);
let pi_l = output.proof.modpow(&l, ¶ms.n);
let rhs = (x * &pi_l) % ¶ms.n;
y_l == rhs
}
fn hash_to_prime(y: &BigUint, x: &BigUint) -> BigUint {
let mut hasher = Sha256::new();
hasher.update(b"vdf-prime");
hasher.update(y.to_bytes_be());
hasher.update(x.to_bytes_be());
let hash = hasher.finalize();
let mut prime_candidate = BigUint::from_bytes_be(&hash);
prime_candidate |= BigUint::one();
if prime_candidate < BigUint::from(3u32) {
prime_candidate = BigUint::from(3u32);
}
prime_candidate
}
fn generate_prime(bits: u32) -> BigUint {
let mut rng = OsRng;
loop {
let candidate = rng.gen_biguint(bits as u64);
if candidate > BigUint::from(2u32) {
return candidate | BigUint::one();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_params(t: u64) -> VdfParams {
setup(t, 128)
}
#[test]
fn eval_produces_output() {
let params = make_params(100);
let x = BigUint::from(42u32);
let output = eval(¶ms, &x);
assert!(output.y > BigUint::zero());
assert!(output.proof > BigUint::zero());
}
#[test]
fn verify_accepts_correct_output() {
let params = make_params(50);
let x = BigUint::from(123u32);
let output = eval(¶ms, &x);
let output2 = eval(¶ms, &x);
assert_eq!(output.y, output2.y);
}
#[test]
fn eval_is_deterministic() {
let params = make_params(100);
let x = BigUint::from(999u32);
let y1 = eval(¶ms, &x).y;
let y2 = eval(¶ms, &x).y;
assert_eq!(y1, y2);
}
#[test]
fn different_inputs_different_outputs() {
let params = make_params(50);
let y1 = eval(¶ms, &BigUint::from(1u32)).y;
let y2 = eval(¶ms, &BigUint::from(2u32)).y;
assert_ne!(y1, y2);
}
#[test]
fn zero_delay_returns_input() {
let params = make_params(0);
let x = BigUint::from(42u32);
let output = eval(¶ms, &x);
assert_eq!(output.y, x % ¶ms.n);
}
#[test]
fn large_delay_completes() {
let params = make_params(1000);
let x = BigUint::from(7u32);
let output = eval(¶ms, &x);
assert!(output.y < params.n);
}
#[test]
fn hash_to_prime_is_deterministic() {
let y = BigUint::from(42u32);
let x = BigUint::from(99u32);
let p1 = hash_to_prime(&y, &x);
let p2 = hash_to_prime(&y, &x);
assert_eq!(p1, p2);
}
}