use blake3::Hasher;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum VdfError {
#[error("Invalid proof")]
InvalidProof,
#[error("Invalid parameters")]
InvalidParameters,
#[error("Iteration count must be positive")]
InvalidIterations,
}
pub type VdfResult<T> = Result<T, VdfError>;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VdfParams {
pub iterations: u64,
}
impl VdfParams {
pub fn new(iterations: u64) -> Self {
assert!(iterations > 0);
Self { iterations }
}
pub fn from_duration_ms(target_ms: u64) -> Self {
const ITERATIONS_PER_MS: u64 = 10_000;
Self {
iterations: target_ms * ITERATIONS_PER_MS,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct VdfOutput {
pub value: Vec<u8>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VdfProof {
checkpoints: Vec<Vec<u8>>,
output: Vec<u8>,
}
pub fn vdf_compute(params: &VdfParams, input: &[u8]) -> (VdfOutput, VdfProof) {
let mut current = hash_input(input);
let mut checkpoints = Vec::new();
let checkpoint_interval = (params.iterations / 10).max(1);
for i in 0..params.iterations {
current = hash_step(¤t);
if i > 0 && (i + 1) % checkpoint_interval == 0 {
checkpoints.push(current.clone());
}
}
let output = VdfOutput {
value: current.clone(),
};
let proof = VdfProof {
checkpoints,
output: current,
};
(output, proof)
}
pub fn vdf_verify(
params: &VdfParams,
input: &[u8],
output: &VdfOutput,
proof: &VdfProof,
) -> VdfResult<()> {
if output.value != proof.output {
return Err(VdfError::InvalidProof);
}
let mut current = hash_input(input);
let checkpoint_interval = (params.iterations / 10).max(1);
let mut checkpoint_idx = 0;
for i in 0..params.iterations {
current = hash_step(¤t);
if i > 0 && (i + 1) % checkpoint_interval == 0 {
if checkpoint_idx >= proof.checkpoints.len() {
return Err(VdfError::InvalidProof);
}
if current != proof.checkpoints[checkpoint_idx] {
return Err(VdfError::InvalidProof);
}
checkpoint_idx += 1;
}
}
if current != proof.output {
return Err(VdfError::InvalidProof);
}
Ok(())
}
pub fn vdf_randomness_beacon(seed: &[u8], iterations: u64) -> Vec<u8> {
let params = VdfParams::new(iterations);
let (output, _proof) = vdf_compute(¶ms, seed);
output.value
}
fn hash_input(input: &[u8]) -> Vec<u8> {
let mut hasher = Hasher::new();
hasher.update(input);
hasher.finalize().as_bytes().to_vec()
}
fn hash_step(data: &[u8]) -> Vec<u8> {
let mut hasher = Hasher::new();
hasher.update(data);
hasher.finalize().as_bytes().to_vec()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vdf_basic() {
let params = VdfParams::new(100);
let input = b"test_input";
let (output, proof) = vdf_compute(¶ms, input);
assert!(vdf_verify(¶ms, input, &output, &proof).is_ok());
}
#[test]
fn test_vdf_deterministic() {
let params = VdfParams::new(100);
let input = b"test_input";
let (output1, _) = vdf_compute(¶ms, input);
let (output2, _) = vdf_compute(¶ms, input);
assert_eq!(output1.value, output2.value);
}
#[test]
fn test_vdf_different_inputs() {
let params = VdfParams::new(100);
let (output1, _) = vdf_compute(¶ms, b"input1");
let (output2, _) = vdf_compute(¶ms, b"input2");
assert_ne!(output1.value, output2.value);
}
#[test]
fn test_vdf_different_iterations() {
let input = b"test_input";
let params1 = VdfParams::new(100);
let params2 = VdfParams::new(200);
let (output1, _) = vdf_compute(¶ms1, input);
let (output2, _) = vdf_compute(¶ms2, input);
assert_ne!(output1.value, output2.value);
}
#[test]
fn test_vdf_invalid_proof() {
let params = VdfParams::new(100);
let input = b"test_input";
let (output, mut proof) = vdf_compute(¶ms, input);
if !proof.checkpoints.is_empty() {
proof.checkpoints[0][0] ^= 1;
}
assert!(vdf_verify(¶ms, input, &output, &proof).is_err());
}
#[test]
fn test_vdf_serialization() {
let params = VdfParams::new(100);
let input = b"test_input";
let (output, proof) = vdf_compute(¶ms, input);
let params_bytes = crate::codec::encode(¶ms).unwrap();
let output_bytes = crate::codec::encode(&output).unwrap();
let proof_bytes = crate::codec::encode(&proof).unwrap();
let params2: VdfParams = crate::codec::decode(¶ms_bytes).unwrap();
let output2: VdfOutput = crate::codec::decode(&output_bytes).unwrap();
let proof2: VdfProof = crate::codec::decode(&proof_bytes).unwrap();
assert!(vdf_verify(¶ms2, input, &output2, &proof2).is_ok());
}
#[test]
fn test_vdf_from_duration() {
let params = VdfParams::from_duration_ms(10);
assert_eq!(params.iterations, 100_000);
let params2 = VdfParams::from_duration_ms(100);
assert_eq!(params2.iterations, 1_000_000);
}
#[test]
fn test_vdf_randomness_beacon() {
let seed = b"beacon_seed";
let output1 = vdf_randomness_beacon(seed, 1000);
let output2 = vdf_randomness_beacon(seed, 1000);
assert_eq!(output1, output2);
let output3 = vdf_randomness_beacon(b"different_seed", 1000);
assert_ne!(output1, output3);
}
#[test]
fn test_vdf_output_length() {
let params = VdfParams::new(100);
let input = b"test";
let (output, _) = vdf_compute(¶ms, input);
assert_eq!(output.value.len(), 32);
}
#[test]
fn test_vdf_large_iterations() {
let params = VdfParams::new(10_000);
let input = b"large_test";
let (output, proof) = vdf_compute(¶ms, input);
assert!(vdf_verify(¶ms, input, &output, &proof).is_ok());
assert!(!proof.checkpoints.is_empty());
}
}