use nebu::Goldilocks;
#[derive(Clone, Debug, PartialEq)]
pub struct VdfProof {
pub input: u64,
pub output: u64,
pub iterations: u64,
}
pub fn evaluate(input: u64, iterations: u64) -> VdfProof {
let mut x = Goldilocks::new(input);
if x.is_zero() {
x = Goldilocks::ONE;
}
for _ in 0..iterations {
x = x.square();
}
VdfProof { input, output: x.as_u64(), iterations }
}
pub fn verify(proof: &VdfProof) -> bool {
if proof.iterations == 0 {
return proof.input == proof.output || (proof.input == 0 && proof.output == 1);
}
evaluate(proof.input, proof.iterations).output == proof.output
}
pub fn challenge_from_hash(hash: &[u8; 32]) -> u64 {
let mut val: u64 = 0;
for (i, &byte) in hash.iter().take(8).enumerate() {
val |= (byte as u64) << (i * 8);
}
if val == 0 { 1 } else { val }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vdf_evaluate_and_verify() {
let proof = evaluate(42, 1000);
assert!(verify(&proof));
assert_ne!(proof.output, 42);
}
#[test]
fn vdf_is_deterministic() {
assert_eq!(evaluate(42, 1000), evaluate(42, 1000));
}
#[test]
fn vdf_tampered_output_fails() {
let mut proof = evaluate(42, 1000);
proof.output ^= 1;
assert!(!verify(&proof));
}
#[test]
fn vdf_tampered_iterations_fails() {
let mut proof = evaluate(42, 1000);
proof.iterations += 1;
assert!(!verify(&proof));
}
#[test]
fn vdf_sequential_property() {
let full = evaluate(7, 2000);
let half = evaluate(evaluate(7, 1000).output, 1000);
assert_eq!(full.output, half.output);
}
#[test]
fn vdf_zero_iterations() {
let proof = evaluate(42, 0);
assert_eq!(proof.input, proof.output);
assert!(verify(&proof));
}
#[test]
fn challenge_from_hash_is_deterministic() {
let h = [0xab; 32];
assert_eq!(challenge_from_hash(&h), challenge_from_hash(&h));
assert_ne!(challenge_from_hash(&h), 0);
}
}