Skip to main content

miden_crypto/
lib.rs

1#![no_std]
2#![allow(clippy::chunks_exact_to_as_chunks)]
3
4#[macro_use]
5extern crate alloc;
6#[cfg(feature = "std")]
7extern crate std;
8
9pub mod aead;
10pub mod dsa;
11pub mod ecdh;
12pub mod hash;
13pub mod ies;
14pub mod merkle;
15pub mod rand;
16pub mod utils;
17
18// RE-EXPORTS
19// ================================================================================================
20pub use miden_field::{Felt, Word, WordError, word};
21
22pub mod field {
23    //! Traits and utilities for working with the Goldilocks finite field (i.e.,
24    //! [Felt](super::Felt)).
25
26    pub use miden_field::{
27        Algebra, BasedVectorSpace, Binomial, BinomialExtensionField, BinomiallyExtendable,
28        BoundedPowers, ExtensionAlgebra, ExtensionField, Field, HasTwoAdicBinomialExtension,
29        InjectiveMonomial, Packable, PermutationMonomial, Powers, PrimeCharacteristicRing,
30        PrimeField, PrimeField64, QuotientMap, RawDataSerializable, TwoAdicField,
31        batch_multiplicative_inverse,
32    };
33
34    pub use super::batch_inversion::batch_inversion_allow_zeros;
35}
36
37pub mod parallel {
38    //! Conditional parallel iteration primitives.
39    //!
40    //! When the `concurrent` feature is enabled, this module re-exports parallel iterator
41    //! traits from `p3-maybe-rayon` backed by rayon. Without `concurrent`, these traits
42    //! fall back to sequential iteration.
43    pub use p3_maybe_rayon::prelude::*;
44}
45
46pub mod stark {
47    //! Lifted STARK proving system based on Plonky3.
48    //!
49    //! Sub-modules from `miden-lifted-stark`:
50    //! - [`proof`] — [`proof::StarkProofData`] (wire artifact), [`proof::StarkProof`] (structured
51    //!   view), [`proof::StarkDigest`], [`proof::StarkOutput`], [`proof::TranscriptChallenger`],
52    //!   [`proof::TranscriptData`]
53    //! - [`air`] — AIR traits, builders, symbolic types (includes all of `p3-air`)
54    //! - [`pcs`] — PCS parameters, DEEP + FRI sub-proofs
55    //! - [`lmcs`] — Lifted Merkle commitment scheme
56    //! - [`hasher`] — Stateful hasher primitives
57    //! - [`prover`] — [`ProverInstance::prove`]
58    //! - [`verifier`] — [`VerifierInstance::verify`]
59    //! - [`debug`] — Debug constraint checker for lifted AIRs
60    //!
61    //! Sub-modules from upstream Plonky3:
62    //! - [`challenger`] — Challenge generation (Fiat-Shamir)
63    //! - [`dft`] — DFT implementations
64    //! - [`matrix`] — Dense matrix types
65    //! - [`symmetric`] — Symmetric cryptographic primitives
66
67    // Top-level types from lifted-stark
68    pub use miden_lifted_stark::{
69        GenericStarkConfig, Preprocessed, PreprocessedValidationError, ProverInstance,
70        QuotientRecompositionInputs, StarkConfig, VerifierInstance, log_quotient_degree,
71        quotient_recomposition_inputs,
72    };
73    // Lifted-stark sub-modules (re-exported as-is)
74    pub use miden_lifted_stark::{air, debug, hasher, lmcs, pcs, proof, prover, verifier};
75
76    // Upstream Plonky3: challenger
77    pub mod challenger {
78        pub use p3_challenger::{
79            CanFinalizeDigest, CanObserve, DuplexChallenger, FieldChallenger, GrindingChallenger,
80            HashChallenger, SerializingChallenger64,
81        };
82    }
83
84    // Upstream Plonky3: dft
85    pub mod dft {
86        pub use p3_dft::{Radix2DFTSmallBatch, Radix2DitParallel, TwoAdicSubgroupDft};
87    }
88
89    // Upstream Plonky3: matrix
90    pub mod matrix {
91        pub use p3_matrix::{Matrix, dense::RowMajorMatrix};
92    }
93
94    // Upstream Plonky3: symmetric
95    pub mod symmetric {
96        pub use p3_symmetric::{
97            CompressionFunctionFromHasher, CryptographicPermutation, PaddingFreeSponge,
98            Permutation, SerializingHasher, TruncatedPermutation,
99        };
100    }
101}
102
103// TYPE ALIASES
104// ================================================================================================
105
106/// An alias for a key-value map.
107///
108/// When the `std` feature is enabled, this is an alias for [`std::collections::HashMap`].
109/// Otherwise, this is an alias for [`alloc::collections::BTreeMap`].
110#[cfg(feature = "std")]
111pub type Map<K, V> = std::collections::HashMap<K, V>;
112
113/// An alias for a key-value map.
114///
115/// When the `std` feature is enabled, this is an alias for [`std::collections::HashMap`].
116/// Otherwise, this is an alias for [`alloc::collections::BTreeMap`].
117#[cfg(not(feature = "std"))]
118pub type Map<K, V> = alloc::collections::BTreeMap<K, V>;
119
120#[cfg(not(feature = "std"))]
121pub use alloc::collections::btree_map::Entry as MapEntry;
122#[cfg(not(feature = "std"))]
123pub use alloc::collections::btree_map::IntoIter as MapIntoIter;
124#[cfg(feature = "std")]
125pub use std::collections::hash_map::Entry as MapEntry;
126#[cfg(feature = "std")]
127pub use std::collections::hash_map::IntoIter as MapIntoIter;
128
129/// An alias for a simple set.
130///
131/// When the `std` feature is enabled, this is an alias for [`std::collections::HashSet`].
132/// Otherwise, this is an alias for [`alloc::collections::BTreeSet`].
133#[cfg(feature = "std")]
134pub type Set<V> = std::collections::HashSet<V>;
135
136/// An alias for a simple set.
137///
138/// When the `std` feature is enabled, this is an alias for [`std::collections::HashSet`].
139/// Otherwise, this is an alias for [`alloc::collections::BTreeSet`].
140#[cfg(not(feature = "std"))]
141pub type Set<V> = alloc::collections::BTreeSet<V>;
142
143// CONSTANTS
144// ================================================================================================
145
146/// Field element representing ZERO in the Miden base field.
147pub const ZERO: Felt = Felt::ZERO;
148
149/// Field element representing ONE in the Miden base field.
150pub const ONE: Felt = Felt::ONE;
151
152/// Array of field elements representing word of ZEROs in the Miden base field.
153pub const EMPTY_WORD: Word = Word::new([ZERO; Word::NUM_ELEMENTS]);
154
155// TRAITS
156// ================================================================================================
157
158/// Defines how to compute a commitment to an object represented as a sequence of field elements.
159pub trait SequentialCommit {
160    /// A type of the commitment which must be derivable from [Word].
161    type Commitment: From<Word>;
162
163    /// Computes the commitment to the object.
164    ///
165    /// The default implementation of this function uses Poseidon2 hash function to hash the
166    /// sequence of elements returned from [Self::to_elements()].
167    fn to_commitment(&self) -> Self::Commitment {
168        hash::poseidon2::Poseidon2::hash_elements(&self.to_elements()).into()
169    }
170
171    /// Returns a representation of the object as a sequence of fields elements.
172    fn to_elements(&self) -> alloc::vec::Vec<Felt>;
173}
174
175// BATCH INVERSION
176// ================================================================================================
177
178mod batch_inversion {
179    use p3_maybe_rayon::prelude::*;
180
181    use super::{Felt, ONE, ZERO, field::Field};
182
183    /// Parallel batch inversion using Montgomery's trick, with zeros left unchanged.
184    ///
185    /// Processes chunks in parallel using rayon, each chunk using Montgomery's trick.
186    pub fn batch_inversion_allow_zeros(values: &mut [Felt]) {
187        const CHUNK_SIZE: usize = 1024;
188
189        values.par_chunks_mut(CHUNK_SIZE).for_each(|output_chunk| {
190            let len = output_chunk.len();
191            let mut scratch = [ZERO; CHUNK_SIZE];
192            scratch[..len].copy_from_slice(output_chunk);
193            batch_inversion_helper(&scratch[..len], output_chunk);
194        });
195    }
196
197    /// Montgomery's trick for batch inversion, handling zeros.
198    fn batch_inversion_helper(values: &[Felt], result: &mut [Felt]) {
199        debug_assert_eq!(values.len(), result.len());
200
201        if values.is_empty() {
202            return;
203        }
204
205        // Forward pass: compute cumulative products, skipping zeros
206        let mut last = ONE;
207        for (result, &value) in result.iter_mut().zip(values.iter()) {
208            *result = last;
209            if value != ZERO {
210                last *= value;
211            }
212        }
213
214        // Invert the final cumulative product
215        last = last.inverse();
216
217        // Backward pass: compute individual inverses
218        for i in (0..values.len()).rev() {
219            if values[i] == ZERO {
220                result[i] = ZERO;
221            } else {
222                result[i] *= last;
223                last *= values[i];
224            }
225        }
226    }
227
228    #[cfg(test)]
229    mod tests {
230        use alloc::vec::Vec;
231
232        use super::*;
233
234        #[test]
235        fn test_batch_inversion_allow_zeros() {
236            let mut column = Vec::from([
237                Felt::new_unchecked(2),
238                ZERO,
239                Felt::new_unchecked(4),
240                Felt::new_unchecked(5),
241            ]);
242            batch_inversion_allow_zeros(&mut column);
243
244            assert_eq!(column[0], Felt::new_unchecked(2).inverse());
245            assert_eq!(column[1], ZERO);
246            assert_eq!(column[2], Felt::new_unchecked(4).inverse());
247            assert_eq!(column[3], Felt::new_unchecked(5).inverse());
248        }
249
250        #[test]
251        fn test_batch_inversion_allow_zeros_spans_fixed_chunks() {
252            let mut v: Vec<Felt> = (1_u64..=2050).map(Felt::new_unchecked).collect();
253            let expected: Vec<Felt> = v.iter().copied().map(|x| x.inverse()).collect();
254            batch_inversion_allow_zeros(&mut v);
255            assert_eq!(v, expected);
256        }
257
258        #[test]
259        fn test_batch_inversion_allow_zeros_zero_on_chunk_boundary() {
260            let mut v = vec![Felt::new_unchecked(7); 1025];
261            v[1023] = ZERO;
262            batch_inversion_allow_zeros(&mut v);
263            assert_eq!(v[1023], ZERO);
264            for i in (0..1023).chain(1024..1025) {
265                assert_eq!(v[i], Felt::new_unchecked(7).inverse());
266            }
267        }
268    }
269}
270
271// TESTS
272// ================================================================================================
273
274#[cfg(test)]
275mod tests {
276
277    #[test]
278    #[should_panic]
279    fn debug_assert_is_checked() {
280        // enforce the release checks to always have `RUSTFLAGS="-C debug-assertions"`.
281        //
282        // some upstream tests are performed with `debug_assert`, and we want to assert its
283        // correctness downstream.
284        //
285        // for reference, check
286        // https://github.com/0xMiden/miden-vm/issues/433
287        debug_assert!(false);
288    }
289
290    #[test]
291    #[should_panic]
292    #[allow(arithmetic_overflow)]
293    fn overflow_panics_for_test() {
294        // overflows might be disabled if tests are performed in release mode. these are critical,
295        // mandatory checks as overflows might be attack vectors.
296        //
297        // to enable overflow checks in release mode, ensure `RUSTFLAGS="-C overflow-checks"`
298        let a = 1_u64;
299        let b = 64;
300        assert_ne!(a << b, 0);
301    }
302}