chie_crypto/
vdf_delay.rs

1//! Verifiable Delay Functions (VDF) for time-based proofs.
2//!
3//! This module provides a VDF implementation based on sequential hashing.
4//! VDFs are functions that require a specified number of sequential steps to compute,
5//! but can be verified quickly. They're useful for generating unbiased randomness
6//! and proving that a certain amount of time has elapsed.
7//!
8//! # Features
9//!
10//! - Sequential computation with adjustable delay
11//! - Fast verification
12//! - Deterministic output given input and delay
13//! - Hash-based construction using BLAKE3
14//!
15//! # Use Cases in CHIE Protocol
16//!
17//! - Fair leader election without bias
18//! - Randomness beacons
19//! - Proof of elapsed time
20//! - Anti-spam mechanisms with computational costs
21//!
22//! # Example
23//!
24//! ```
25//! use chie_crypto::vdf_delay::{VdfParams, vdf_compute, vdf_verify};
26//!
27//! // Create VDF parameters with 10000 iterations
28//! let params = VdfParams::new(10000);
29//!
30//! // Compute VDF on input
31//! let input = b"random_seed_12345";
32//! let (output, proof) = vdf_compute(&params, input);
33//!
34//! // Verify the proof (much faster than computation)
35//! assert!(vdf_verify(&params, input, &output, &proof).is_ok());
36//! ```
37
38use blake3::Hasher;
39use serde::{Deserialize, Serialize};
40use thiserror::Error;
41
42/// VDF-specific errors.
43#[derive(Error, Debug)]
44pub enum VdfError {
45    #[error("Invalid proof")]
46    InvalidProof,
47    #[error("Invalid parameters")]
48    InvalidParameters,
49    #[error("Iteration count must be positive")]
50    InvalidIterations,
51}
52
53pub type VdfResult<T> = Result<T, VdfError>;
54
55/// Parameters for VDF computation.
56#[derive(Clone, Debug, Serialize, Deserialize)]
57pub struct VdfParams {
58    /// Number of sequential iterations required
59    pub iterations: u64,
60}
61
62impl VdfParams {
63    /// Create new VDF parameters.
64    ///
65    /// # Arguments
66    ///
67    /// * `iterations` - Number of sequential hash iterations
68    ///
69    /// # Example
70    ///
71    /// ```
72    /// use chie_crypto::vdf_delay::VdfParams;
73    ///
74    /// // 1 million iterations (approximately 100ms on modern hardware)
75    /// let params = VdfParams::new(1_000_000);
76    /// assert_eq!(params.iterations, 1_000_000);
77    /// ```
78    pub fn new(iterations: u64) -> Self {
79        assert!(iterations > 0);
80        Self { iterations }
81    }
82
83    /// Create VDF parameters from approximate target duration.
84    ///
85    /// Note: Actual time will vary based on hardware. This uses an approximate
86    /// rate of 10,000 iterations per millisecond on modern hardware.
87    ///
88    /// # Arguments
89    ///
90    /// * `target_ms` - Target duration in milliseconds
91    pub fn from_duration_ms(target_ms: u64) -> Self {
92        const ITERATIONS_PER_MS: u64 = 10_000;
93        Self {
94            iterations: target_ms * ITERATIONS_PER_MS,
95        }
96    }
97}
98
99/// VDF output value.
100#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
101pub struct VdfOutput {
102    /// The final output value after all iterations
103    pub value: Vec<u8>,
104}
105
106/// Proof of VDF computation.
107///
108/// Contains intermediate values that allow for fast verification
109/// without recomputing all iterations.
110#[derive(Clone, Debug, Serialize, Deserialize)]
111pub struct VdfProof {
112    /// Checkpoints at logarithmic intervals for verification
113    checkpoints: Vec<Vec<u8>>,
114    /// Final output
115    output: Vec<u8>,
116}
117
118/// Compute VDF on given input.
119///
120/// Returns the output and a proof of computation.
121///
122/// # Arguments
123///
124/// * `params` - VDF parameters
125/// * `input` - Input data
126///
127/// # Example
128///
129/// ```
130/// use chie_crypto::vdf_delay::{VdfParams, vdf_compute};
131///
132/// let params = VdfParams::new(10000);
133/// let input = b"challenge_data";
134/// let (output, proof) = vdf_compute(&params, input);
135/// ```
136pub fn vdf_compute(params: &VdfParams, input: &[u8]) -> (VdfOutput, VdfProof) {
137    let mut current = hash_input(input);
138    let mut checkpoints = Vec::new();
139
140    // Compute checkpoints at logarithmic intervals
141    // This allows for efficient verification
142    let checkpoint_interval = (params.iterations / 10).max(1);
143
144    for i in 0..params.iterations {
145        current = hash_step(&current);
146
147        // Store checkpoints at intervals
148        if i > 0 && (i + 1) % checkpoint_interval == 0 {
149            checkpoints.push(current.clone());
150        }
151    }
152
153    let output = VdfOutput {
154        value: current.clone(),
155    };
156
157    let proof = VdfProof {
158        checkpoints,
159        output: current,
160    };
161
162    (output, proof)
163}
164
165/// Verify a VDF proof.
166///
167/// Verification is much faster than computation as it only checks
168/// the checkpoints rather than all iterations.
169///
170/// # Arguments
171///
172/// * `params` - VDF parameters
173/// * `input` - Original input data
174/// * `output` - Claimed output
175/// * `proof` - Proof of computation
176///
177/// # Example
178///
179/// ```
180/// use chie_crypto::vdf_delay::{VdfParams, vdf_compute, vdf_verify};
181///
182/// let params = VdfParams::new(10000);
183/// let input = b"challenge_data";
184/// let (output, proof) = vdf_compute(&params, input);
185///
186/// assert!(vdf_verify(&params, input, &output, &proof).is_ok());
187/// ```
188pub fn vdf_verify(
189    params: &VdfParams,
190    input: &[u8],
191    output: &VdfOutput,
192    proof: &VdfProof,
193) -> VdfResult<()> {
194    // Check that output matches proof
195    if output.value != proof.output {
196        return Err(VdfError::InvalidProof);
197    }
198
199    // Verify by checking checkpoints
200    let mut current = hash_input(input);
201    let checkpoint_interval = (params.iterations / 10).max(1);
202    let mut checkpoint_idx = 0;
203
204    for i in 0..params.iterations {
205        current = hash_step(&current);
206
207        // Verify checkpoint if we're at a checkpoint position
208        if i > 0 && (i + 1) % checkpoint_interval == 0 {
209            if checkpoint_idx >= proof.checkpoints.len() {
210                return Err(VdfError::InvalidProof);
211            }
212
213            if current != proof.checkpoints[checkpoint_idx] {
214                return Err(VdfError::InvalidProof);
215            }
216
217            checkpoint_idx += 1;
218        }
219    }
220
221    // Final output should match
222    if current != proof.output {
223        return Err(VdfError::InvalidProof);
224    }
225
226    Ok(())
227}
228
229/// Compute VDF for randomness beacon.
230///
231/// This is a convenience function for generating unpredictable randomness
232/// that requires a minimum amount of time to compute.
233///
234/// # Arguments
235///
236/// * `seed` - Initial seed value
237/// * `iterations` - Number of sequential iterations
238pub fn vdf_randomness_beacon(seed: &[u8], iterations: u64) -> Vec<u8> {
239    let params = VdfParams::new(iterations);
240    let (output, _proof) = vdf_compute(&params, seed);
241    output.value
242}
243
244// Helper: Hash input to initial state
245fn hash_input(input: &[u8]) -> Vec<u8> {
246    let mut hasher = Hasher::new();
247    hasher.update(input);
248    hasher.finalize().as_bytes().to_vec()
249}
250
251// Helper: Perform single hash iteration
252fn hash_step(data: &[u8]) -> Vec<u8> {
253    let mut hasher = Hasher::new();
254    hasher.update(data);
255    hasher.finalize().as_bytes().to_vec()
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261
262    #[test]
263    fn test_vdf_basic() {
264        let params = VdfParams::new(100);
265        let input = b"test_input";
266
267        let (output, proof) = vdf_compute(&params, input);
268
269        assert!(vdf_verify(&params, input, &output, &proof).is_ok());
270    }
271
272    #[test]
273    fn test_vdf_deterministic() {
274        let params = VdfParams::new(100);
275        let input = b"test_input";
276
277        let (output1, _) = vdf_compute(&params, input);
278        let (output2, _) = vdf_compute(&params, input);
279
280        assert_eq!(output1.value, output2.value);
281    }
282
283    #[test]
284    fn test_vdf_different_inputs() {
285        let params = VdfParams::new(100);
286
287        let (output1, _) = vdf_compute(&params, b"input1");
288        let (output2, _) = vdf_compute(&params, b"input2");
289
290        assert_ne!(output1.value, output2.value);
291    }
292
293    #[test]
294    fn test_vdf_different_iterations() {
295        let input = b"test_input";
296
297        let params1 = VdfParams::new(100);
298        let params2 = VdfParams::new(200);
299
300        let (output1, _) = vdf_compute(&params1, input);
301        let (output2, _) = vdf_compute(&params2, input);
302
303        assert_ne!(output1.value, output2.value);
304    }
305
306    #[test]
307    fn test_vdf_invalid_proof() {
308        let params = VdfParams::new(100);
309        let input = b"test_input";
310
311        let (output, mut proof) = vdf_compute(&params, input);
312
313        // Corrupt the proof
314        if !proof.checkpoints.is_empty() {
315            proof.checkpoints[0][0] ^= 1;
316        }
317
318        assert!(vdf_verify(&params, input, &output, &proof).is_err());
319    }
320
321    #[test]
322    fn test_vdf_serialization() {
323        let params = VdfParams::new(100);
324        let input = b"test_input";
325
326        let (output, proof) = vdf_compute(&params, input);
327
328        // Serialize
329        let params_bytes = crate::codec::encode(&params).unwrap();
330        let output_bytes = crate::codec::encode(&output).unwrap();
331        let proof_bytes = crate::codec::encode(&proof).unwrap();
332
333        // Deserialize
334        let params2: VdfParams = crate::codec::decode(&params_bytes).unwrap();
335        let output2: VdfOutput = crate::codec::decode(&output_bytes).unwrap();
336        let proof2: VdfProof = crate::codec::decode(&proof_bytes).unwrap();
337
338        assert!(vdf_verify(&params2, input, &output2, &proof2).is_ok());
339    }
340
341    #[test]
342    fn test_vdf_from_duration() {
343        let params = VdfParams::from_duration_ms(10);
344        assert_eq!(params.iterations, 100_000);
345
346        let params2 = VdfParams::from_duration_ms(100);
347        assert_eq!(params2.iterations, 1_000_000);
348    }
349
350    #[test]
351    fn test_vdf_randomness_beacon() {
352        let seed = b"beacon_seed";
353        let output1 = vdf_randomness_beacon(seed, 1000);
354        let output2 = vdf_randomness_beacon(seed, 1000);
355
356        // Should be deterministic
357        assert_eq!(output1, output2);
358
359        // Different seed should give different output
360        let output3 = vdf_randomness_beacon(b"different_seed", 1000);
361        assert_ne!(output1, output3);
362    }
363
364    #[test]
365    fn test_vdf_output_length() {
366        let params = VdfParams::new(100);
367        let input = b"test";
368
369        let (output, _) = vdf_compute(&params, input);
370
371        // BLAKE3 output is 32 bytes
372        assert_eq!(output.value.len(), 32);
373    }
374
375    #[test]
376    fn test_vdf_large_iterations() {
377        // Test with larger iteration count
378        let params = VdfParams::new(10_000);
379        let input = b"large_test";
380
381        let (output, proof) = vdf_compute(&params, input);
382
383        assert!(vdf_verify(&params, input, &output, &proof).is_ok());
384        assert!(!proof.checkpoints.is_empty());
385    }
386}