Skip to main content

kyn_vdf/
lib.rs

1//! # kyn-vdf
2//!
3//! A pure Rust, WebAssembly-compatible implementation of Wesolowski Verifiable Delay Function
4//! (VDF) verification over Imaginary Quadratic Class Groups.
5//!
6//! ## Key Features
7//! - **Pure Rust / Zero FFI**: No C/C++ compiler, `libgmp`, or OS dependencies required.
8//! - **WebAssembly Native**: Compiles to `wasm32-unknown-unknown` for in-browser and mobile
9//!   light client verification.
10//! - **Shanks' NUCOMP / NUDUPL**: Sub-quadratic binary quadratic form composition and squaring
11//!   with partial Euclidean reduction.
12//! - **Chia-Compatible**: 100% test-vector compatible with the Chia Network VDF specification.
13//! - **$\mathcal{O}(\log T)$ Verification**: Verification time is constant with respect to $T$
14//!   (bounded by the 264-bit Fiat-Shamir prime $B$, not the iteration count).
15//! - **No Panics**: All fallible operations return `Result<_, KynVdfError>` — safe for WASM
16//!   and adversarial inputs.
17//!
18//! ## Quick Start
19//! ```rust
20//! use kyn_vdf::verify_chia_vdf;
21//!
22//! # fn example(challenge: &[u8], proof_bytes: &[u8]) -> Result<(), kyn_vdf::KynVdfError> {
23//! let is_valid = verify_chia_vdf(challenge, proof_bytes, 100_000, 1024)?;
24//! assert!(is_valid);
25//! # Ok(())
26//! # }
27//! ```
28
29pub mod chia;
30pub mod error;
31pub mod math;
32
33#[cfg(target_arch = "wasm32")]
34pub mod wasm;
35
36pub use chia::{
37    create_discriminant, deserialize_form, get_b, hash_prime, is_probable_prime, serialize_form,
38    verify_wesolowski, CompressedForm,
39};
40pub use error::KynVdfError;
41pub use math::{isqrt_fourth, xgcd_partial, Form};
42
43/// Verifies a Chia-compatible Wesolowski VDF proof from raw byte slices.
44///
45/// This is the primary entry point for most callers. It handles discriminant
46/// generation, form deserialization, and the Wesolowski verification equation
47/// in a single call.
48///
49/// # Parameters
50/// - `challenge_seed`: The challenge byte slice used to derive the discriminant and
51///   generator form. Typically 32 bytes (e.g. a block hash).
52/// - `proof_bytes`: The serialized proof in Chia wire format — exactly 200 bytes
53///   containing the VDF output `y` (bytes 0–99) concatenated with the proof `π`
54///   (bytes 100–199), each in 100-byte BQFC format.
55/// - `iterations`: Number of sequential squarings $T$ that were evaluated.
56///   Must be ≥ 1.
57/// - `discriminant_size_bits`: Bit-size of the class group discriminant (e.g. `1024`).
58///   Must be a non-zero multiple of 8. Use `1024` unless you have a specific reason
59///   to use a different size.
60///
61/// # Returns
62/// - `Ok(true)` — proof is mathematically valid.
63/// - `Ok(false)` — proof is rejected (valid inputs but incorrect proof).
64/// - `Err(KynVdfError)` — inputs are malformed (wrong lengths, invalid seed, etc.).
65///
66/// # Errors
67/// - [`KynVdfError::InvalidIterations`] if `iterations == 0`.
68/// - [`KynVdfError::InvalidProofLength`] if `proof_bytes.len() < 200`.
69/// - [`KynVdfError::InvalidDiscriminantSize`] if `discriminant_size_bits` is invalid.
70/// - [`KynVdfError::FormDeserializationError`] if the proof bytes are corrupted.
71/// - [`KynVdfError::InvalidDiscriminantIdentity`] if a form fails the discriminant check.
72pub fn verify_chia_vdf(
73    challenge_seed: &[u8],
74    proof_bytes: &[u8],
75    iterations: u64,
76    discriminant_size_bits: usize,
77) -> Result<bool, KynVdfError> {
78    if iterations == 0 {
79        return Err(KynVdfError::InvalidIterations(0));
80    }
81    if proof_bytes.len() < 200 {
82        return Err(KynVdfError::InvalidProofLength {
83            expected: 200,
84            actual: proof_bytes.len(),
85        });
86    }
87
88    let d = create_discriminant(challenge_seed, discriminant_size_bits)?;
89    let x = Form::generator(&d).ok_or(KynVdfError::InvalidDiscriminantIdentity)?;
90
91    let y_form = deserialize_form(&d, &proof_bytes[0..100])?;
92    let proof_form = deserialize_form(&d, &proof_bytes[100..200])?;
93
94    verify_wesolowski(&d, &x, &y_form, &proof_form, iterations)
95}
96
97/// Pure Rust Wesolowski VDF verifier.
98///
99/// A thin stateful wrapper around [`verify_chia_vdf`] that holds the discriminant
100/// size so callers don't need to pass it on every verification call.
101///
102/// # Example
103/// ```rust
104/// use kyn_vdf::KynVdfVerifier;
105///
106/// # fn example(challenge: &[u8], proof: &[u8]) -> Result<(), kyn_vdf::KynVdfError> {
107/// let verifier = KynVdfVerifier::new(); // 1024-bit discriminant
108/// let is_valid = verifier.verify(challenge, proof, 100_000)?;
109/// # Ok(())
110/// # }
111/// ```
112#[derive(Debug, Clone, Default)]
113pub struct KynVdfVerifier {
114    /// Bit-size of the class group discriminant (default: 1024).
115    discriminant_size_bits: usize,
116}
117
118impl KynVdfVerifier {
119    /// Creates a new `KynVdfVerifier` with the standard 1024-bit discriminant.
120    ///
121    /// This matches the discriminant size used by the Chia Network mainnet VDF.
122    pub fn new() -> Self {
123        Self {
124            discriminant_size_bits: 1024,
125        }
126    }
127
128    /// Creates a new `KynVdfVerifier` with a custom discriminant bit-size.
129    ///
130    /// Use this when verifying proofs generated with a non-standard discriminant size.
131    /// `discriminant_size_bits` must be a non-zero multiple of 8.
132    pub fn with_bits(discriminant_size_bits: usize) -> Self {
133        Self {
134            discriminant_size_bits,
135        }
136    }
137
138    /// Verifies a Wesolowski VDF proof.
139    ///
140    /// Delegates to [`verify_chia_vdf`] with this verifier's configured discriminant size.
141    ///
142    /// # Errors
143    /// See [`verify_chia_vdf`] for the full list of possible errors.
144    pub fn verify(
145        &self,
146        challenge: &[u8],
147        proof_bytes: &[u8],
148        iterations: u64,
149    ) -> Result<bool, KynVdfError> {
150        verify_chia_vdf(challenge, proof_bytes, iterations, self.discriminant_size_bits)
151    }
152}