Skip to main content

miden_core/utils/
mod.rs

1use alloc::vec::Vec;
2use core::ops::{Bound, Range};
3
4use crate::{Felt, Word, crypto::hash::Blake3_256, field::PrimeCharacteristicRing};
5
6// RE-EXPORTS
7// ================================================================================================
8
9mod col_matrix;
10pub use col_matrix::ColMatrix;
11#[cfg(feature = "std")]
12pub use miden_crypto::utils::ReadAdapter;
13pub use miden_crypto::{
14    stark::matrix::{Matrix, RowMajorMatrix},
15    utils::{flatten_slice_elements, flatten_vector_elements, group_slice_elements, uninit_vector},
16};
17pub use miden_formatting::hex::{DisplayHex, ToHex, to_hex};
18pub use miden_utils_indexing::{
19    CsrMatrix, CsrValidationError, DenseIdMap, Idx, IndexVec, IndexedVecError, LookupByIdx,
20    newtype_id,
21};
22
23// TO ELEMENTS
24// ================================================================================================
25
26pub trait ToElements {
27    fn to_elements(&self) -> Vec<Felt>;
28}
29
30impl<const N: usize> ToElements for [u64; N] {
31    fn to_elements(&self) -> Vec<Felt> {
32        self.iter().map(|&v| Felt::from_u64(v)).collect()
33    }
34}
35
36impl ToElements for Vec<u64> {
37    fn to_elements(&self) -> Vec<Felt> {
38        self.iter().map(|&v| Felt::from_u64(v)).collect()
39    }
40}
41
42// TO WORD
43// ================================================================================================
44
45/// Hashes the provided string using the BLAKE3 hash function and converts the resulting digest into
46/// a [`Word`].
47pub fn hash_string_to_word<'a>(value: impl Into<&'a str>) -> Word {
48    let digest_bytes: [u8; 32] = Blake3_256::hash(value.into().as_bytes()).into();
49    [
50        Felt::new(u64::from_le_bytes(digest_bytes[0..8].try_into().unwrap())),
51        Felt::new(u64::from_le_bytes(digest_bytes[8..16].try_into().unwrap())),
52        Felt::new(u64::from_le_bytes(digest_bytes[16..24].try_into().unwrap())),
53        Felt::new(u64::from_le_bytes(digest_bytes[24..32].try_into().unwrap())),
54    ]
55    .into()
56}
57
58// INTO BYTES
59// ================================================================================================
60
61pub trait IntoBytes<const N: usize> {
62    fn into_bytes(self) -> [u8; N];
63}
64
65impl IntoBytes<32> for [Felt; 4] {
66    fn into_bytes(self) -> [u8; 32] {
67        let mut result = [0; 32];
68
69        result[..8].copy_from_slice(&self[0].as_canonical_u64().to_le_bytes());
70        result[8..16].copy_from_slice(&self[1].as_canonical_u64().to_le_bytes());
71        result[16..24].copy_from_slice(&self[2].as_canonical_u64().to_le_bytes());
72        result[24..].copy_from_slice(&self[3].as_canonical_u64().to_le_bytes());
73
74        result
75    }
76}
77
78// RANGE
79// ================================================================================================
80
81/// Returns a [Range] initialized with the specified `start` and with `end` set to `start` + `len`.
82pub const fn range(start: usize, len: usize) -> Range<usize> {
83    Range { start, end: start + len }
84}
85
86/// Converts and parses a [Bound] into an included u64 value.
87pub fn bound_into_included_u64<I>(bound: Bound<&I>, is_start: bool) -> u64
88where
89    I: Clone + Into<u64>,
90{
91    match bound {
92        Bound::Excluded(i) => i.clone().into().saturating_sub(1),
93        Bound::Included(i) => i.clone().into(),
94        Bound::Unbounded => {
95            if is_start {
96                0
97            } else {
98                u64::MAX
99            }
100        },
101    }
102}
103
104// BYTE CONVERSIONS
105// ================================================================================================
106
107/// Number of bytes packed into each u32 field element.
108///
109/// Used for converting between byte arrays and u32-packed field elements in memory.
110const BYTES_PER_U32: usize = core::mem::size_of::<u32>();
111
112/// Converts bytes to field elements using u32 packing in little-endian format.
113///
114/// Each field element contains a u32 value representing up to 4 bytes. If the byte length
115/// is not a multiple of 4, the final field element is zero-padded.
116///
117/// This is commonly used by precompile handlers (Keccak256, ECDSA) to convert byte data
118/// into field element commitments.
119///
120/// # Arguments
121/// - `bytes`: The byte slice to convert
122///
123/// # Returns
124/// A vector of field elements, each containing 4 bytes packed in little-endian order.
125///
126/// # Examples
127/// ```
128/// # use miden_core::{Felt, utils::bytes_to_packed_u32_elements, field::PrimeCharacteristicRing};
129/// let bytes = vec![0x01, 0x02, 0x03, 0x04, 0x05];
130/// let felts = bytes_to_packed_u32_elements(&bytes);
131/// assert_eq!(felts, vec![Felt::from_u32(0x04030201_u32), Felt::from_u32(0x00000005_u32)]);
132/// ```
133pub fn bytes_to_packed_u32_elements(bytes: &[u8]) -> Vec<Felt> {
134    bytes
135        .chunks(BYTES_PER_U32)
136        .map(|chunk| {
137            // Pack up to 4 bytes into a u32 in little-endian format
138            let mut packed = [0u8; BYTES_PER_U32];
139            packed[..chunk.len()].copy_from_slice(chunk);
140            Felt::from_u32(u32::from_le_bytes(packed))
141        })
142        .collect()
143}
144
145/// Converts u32-packed field elements back to bytes in little-endian format.
146///
147/// This is the inverse of [`bytes_to_packed_u32_elements`]. Each field element is expected
148/// to contain a u32 value, which is unpacked into 4 bytes.
149///
150/// # Arguments
151/// - `elements`: The field elements to convert
152///
153/// # Returns
154/// A vector of bytes representing the unpacked data.
155///
156/// # Examples
157/// ```
158/// # use miden_core::{Felt, utils::{bytes_to_packed_u32_elements, packed_u32_elements_to_bytes}};
159/// let original = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
160/// let elements = bytes_to_packed_u32_elements(&original);
161/// let bytes = packed_u32_elements_to_bytes(&elements);
162/// assert_eq!(bytes, original);
163/// ```
164pub fn packed_u32_elements_to_bytes(elements: &[Felt]) -> Vec<u8> {
165    elements
166        .iter()
167        .flat_map(|felt| {
168            let value = felt.as_canonical_u64() as u32;
169            value.to_le_bytes()
170        })
171        .collect()
172}
173
174// TESTS
175// ================================================================================================
176
177#[cfg(test)]
178mod tests {
179    use proptest::prelude::*;
180
181    use super::*;
182
183    proptest! {
184        #[test]
185        fn proptest_packed_u32_elements_roundtrip(values in prop::collection::vec(any::<u32>(), 0..100)) {
186            // Convert u32 values to Felts
187            let felts: Vec<Felt> = values.iter().map(|&v| Felt::from_u32(v)).collect();
188
189            // Roundtrip: Felts -> bytes -> Felts
190            let bytes = packed_u32_elements_to_bytes(&felts);
191            let roundtrip_felts = bytes_to_packed_u32_elements(&bytes);
192
193            // Should be equal
194            prop_assert_eq!(felts, roundtrip_felts);
195        }
196    }
197
198    #[test]
199    #[should_panic]
200    fn debug_assert_is_checked() {
201        // enforce the release checks to always have `RUSTFLAGS="-C debug-assertions".
202        //
203        // some upstream tests are performed with `debug_assert`, and we want to assert its
204        // correctness downstream.
205        //
206        // for reference, check
207        // https://github.com/0xMiden/miden-vm/issues/433
208        debug_assert!(false);
209    }
210}