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}