miden_crypto/word/
mod.rs

1//! A [Word] type used in the Miden protocol and associated utilities.
2
3use alloc::{string::String, vec::Vec};
4use core::{
5    cmp::Ordering,
6    fmt::Display,
7    hash::{Hash, Hasher},
8    ops::{Deref, DerefMut, Index, IndexMut, Range},
9    slice,
10};
11
12use thiserror::Error;
13use winter_crypto::Digest;
14use winter_math::FieldElement;
15
16const WORD_SIZE_FELT: usize = 4;
17const WORD_SIZE_BYTES: usize = 32;
18
19use super::{Felt, StarkField, ZERO};
20use crate::{
21    rand::Randomizable,
22    utils::{
23        ByteReader, ByteWriter, Deserializable, DeserializationError, HexParseError, Serializable,
24        bytes_to_hex_string, hex_to_bytes,
25    },
26};
27
28mod lexicographic;
29pub use lexicographic::LexicographicWord;
30
31#[cfg(test)]
32mod tests;
33
34// WORD
35// ================================================================================================
36
37/// A unit of data consisting of 4 field elements.
38#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
39#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
40#[cfg_attr(feature = "serde", serde(into = "String", try_from = "&str"))]
41pub struct Word([Felt; WORD_SIZE_FELT]);
42
43impl Word {
44    /// The serialized size of the word in bytes.
45    pub const SERIALIZED_SIZE: usize = WORD_SIZE_BYTES;
46
47    /// Creates a new [`Word`] from the given field elements.
48    pub const fn new(value: [Felt; WORD_SIZE_FELT]) -> Self {
49        Self(value)
50    }
51
52    /// Parses a hex string into a new [`Word`].
53    ///
54    /// The input must contain valid hex prefixed with `0x`. The input after the prefix
55    /// must contain between 0 and 64 characters (inclusive).
56    ///
57    /// The input is interpreted to have little-endian byte ordering. Nibbles are interpreted
58    /// to have big-endian ordering so that "0x10" represents Felt::new(16), not Felt::new(1).
59    ///
60    /// This function is usually used via the `word!` macro.
61    ///
62    /// ```
63    /// use miden_crypto::{Felt, Word, word};
64    /// let word = word!("0x1000000000000000200000000000000030000000000000004000000000000000");
65    /// assert_eq!(word, Word::new([Felt::new(16), Felt::new(32), Felt::new(48), Felt::new(64)]));
66    /// ```
67    pub const fn parse(hex: &str) -> Result<Self, &'static str> {
68        const fn parse_hex_digit(digit: u8) -> Result<u8, &'static str> {
69            match digit {
70                b'0'..=b'9' => Ok(digit - b'0'),
71                b'A'..=b'F' => Ok(digit - b'A' + 0x0a),
72                b'a'..=b'f' => Ok(digit - b'a' + 0x0a),
73                _ => Err("Invalid hex character"),
74            }
75        }
76        // Enforce and skip the '0x' prefix.
77        let hex_bytes = match hex.as_bytes() {
78            [b'0', b'x', rest @ ..] => rest,
79            _ => return Err("Hex string must have a \"0x\" prefix"),
80        };
81
82        if hex_bytes.len() > 64 {
83            return Err("Hex string has more than 64 characters");
84        }
85
86        let mut felts = [0u64; 4];
87        let mut i = 0;
88        while i < hex_bytes.len() {
89            let hex_digit = match parse_hex_digit(hex_bytes[i]) {
90                // SAFETY: u8 cast to u64 is safe. We cannot use u64::from in const context so we
91                // are forced to cast.
92                Ok(v) => v as u64,
93                Err(e) => return Err(e),
94            };
95
96            // This digit's nibble offset within the felt. We need to invert the nibbles per
97            // byte to ensure little-endian ordering i.e. ABCD -> BADC.
98            let inibble = if i.is_multiple_of(2) {
99                (i + 1) % 16
100            } else {
101                (i - 1) % 16
102            };
103
104            let value = hex_digit << (inibble * 4);
105            felts[i / 2 / 8] += value;
106
107            i += 1;
108        }
109
110        // Ensure each felt is within bounds as `Felt::new` silently wraps around.
111        // This matches the behavior of `Word::try_from(String)`.
112        let mut idx = 0;
113        while idx < felts.len() {
114            if felts[idx] >= Felt::MODULUS {
115                return Err("Felt overflow");
116            }
117            idx += 1;
118        }
119
120        Ok(Self::new([
121            Felt::new(felts[0]),
122            Felt::new(felts[1]),
123            Felt::new(felts[2]),
124            Felt::new(felts[3]),
125        ]))
126    }
127
128    /// Returns a new [Word] consisting of four ZERO elements.
129    pub const fn empty() -> Self {
130        Self([Felt::ZERO; WORD_SIZE_FELT])
131    }
132
133    /// Returns true if the word consists of four ZERO elements.
134    pub const fn is_empty(&self) -> bool {
135        const ZERO_FELT_INNER: u64 = Felt::ZERO.inner();
136
137        self.0[0].inner() == ZERO_FELT_INNER
138            && self.0[1].inner() == ZERO_FELT_INNER
139            && self.0[2].inner() == ZERO_FELT_INNER
140            && self.0[3].inner() == ZERO_FELT_INNER
141    }
142
143    /// Returns the word as a slice of field elements.
144    pub fn as_elements(&self) -> &[Felt] {
145        self.as_ref()
146    }
147
148    /// Returns the word as a byte array.
149    pub fn as_bytes(&self) -> [u8; WORD_SIZE_BYTES] {
150        let mut result = [0; WORD_SIZE_BYTES];
151
152        result[..8].copy_from_slice(&self.0[0].as_int().to_le_bytes());
153        result[8..16].copy_from_slice(&self.0[1].as_int().to_le_bytes());
154        result[16..24].copy_from_slice(&self.0[2].as_int().to_le_bytes());
155        result[24..].copy_from_slice(&self.0[3].as_int().to_le_bytes());
156
157        result
158    }
159
160    /// Returns an iterator over the elements of multiple words.
161    pub(crate) fn words_as_elements_iter<'a, I>(words: I) -> impl Iterator<Item = &'a Felt>
162    where
163        I: Iterator<Item = &'a Self>,
164    {
165        words.flat_map(|d| d.0.iter())
166    }
167
168    /// Returns all elements of multiple words as a slice.
169    pub fn words_as_elements(words: &[Self]) -> &[Felt] {
170        let p = words.as_ptr();
171        let len = words.len() * WORD_SIZE_FELT;
172        unsafe { slice::from_raw_parts(p as *const Felt, len) }
173    }
174
175    /// Returns hexadecimal representation of this word prefixed with `0x`.
176    pub fn to_hex(&self) -> String {
177        bytes_to_hex_string(self.as_bytes())
178    }
179
180    /// Returns internal elements of this word as a vector.
181    pub fn to_vec(&self) -> Vec<Felt> {
182        self.0.to_vec()
183    }
184}
185
186impl Hash for Word {
187    fn hash<H: Hasher>(&self, state: &mut H) {
188        state.write(&self.as_bytes());
189    }
190}
191
192impl Digest for Word {
193    fn as_bytes(&self) -> [u8; WORD_SIZE_BYTES] {
194        self.as_bytes()
195    }
196}
197
198impl Deref for Word {
199    type Target = [Felt; WORD_SIZE_FELT];
200
201    fn deref(&self) -> &Self::Target {
202        &self.0
203    }
204}
205
206impl DerefMut for Word {
207    fn deref_mut(&mut self) -> &mut Self::Target {
208        &mut self.0
209    }
210}
211
212impl Index<usize> for Word {
213    type Output = Felt;
214
215    fn index(&self, index: usize) -> &Self::Output {
216        &self.0[index]
217    }
218}
219
220impl IndexMut<usize> for Word {
221    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
222        &mut self.0[index]
223    }
224}
225
226impl Index<Range<usize>> for Word {
227    type Output = [Felt];
228
229    fn index(&self, index: Range<usize>) -> &Self::Output {
230        &self.0[index]
231    }
232}
233
234impl IndexMut<Range<usize>> for Word {
235    fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
236        &mut self.0[index]
237    }
238}
239
240impl Ord for Word {
241    fn cmp(&self, other: &Self) -> Ordering {
242        // Compare the inner u64 of both elements.
243        //
244        // It will iterate the elements and will return the first computation different than
245        // `Equal`. Otherwise, the ordering is equal.
246        //
247        // Finally, we use `Felt::inner` instead of `Felt::as_int` so we avoid performing a
248        // montgomery reduction for every limb. That is safe because every inner element of the
249        // word is guaranteed to be in its canonical form (that is, `x in [0,p)`).
250        //
251        // Because we don't perform Montgomery reduction, we must iterate over, and compare,
252        // each element. A simple bytestring comparison would be inappropriate because the `Word`s
253        // are represented in "lexicographical" order.
254        self.0.iter().map(Felt::inner).zip(other.0.iter().map(Felt::inner)).fold(
255            Ordering::Equal,
256            |ord, (a, b)| match ord {
257                Ordering::Equal => a.cmp(&b),
258                _ => ord,
259            },
260        )
261    }
262}
263
264impl PartialOrd for Word {
265    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
266        Some(self.cmp(other))
267    }
268}
269
270impl Display for Word {
271    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
272        write!(f, "{}", self.to_hex())
273    }
274}
275
276impl Randomizable for Word {
277    const VALUE_SIZE: usize = WORD_SIZE_BYTES;
278
279    fn from_random_bytes(bytes: &[u8]) -> Option<Self> {
280        let bytes_array: Option<[u8; 32]> = bytes.try_into().ok();
281        if let Some(bytes_array) = bytes_array {
282            Self::try_from(bytes_array).ok()
283        } else {
284            None
285        }
286    }
287}
288
289// CONVERSIONS: FROM WORD
290// ================================================================================================
291
292/// Errors that can occur when working with a [Word].
293#[derive(Debug, Error)]
294pub enum WordError {
295    /// Hex-encoded field elements parsed are invalid.
296    #[error("hex encoded values of a word are invalid")]
297    HexParse(#[from] HexParseError),
298    /// Field element conversion failed due to invalid value.
299    #[error("failed to convert to field element: {0}")]
300    InvalidFieldElement(String),
301    /// Failed to convert a slice to an array of expected length.
302    #[error("invalid input length: expected {1} {0}, but received {2}")]
303    InvalidInputLength(&'static str, usize, usize),
304    /// Failed to convert the word's field elements to the specified type.
305    #[error("failed to convert the word's field elements to type {0}")]
306    TypeConversion(&'static str),
307}
308
309impl TryFrom<&Word> for [bool; WORD_SIZE_FELT] {
310    type Error = WordError;
311
312    fn try_from(value: &Word) -> Result<Self, Self::Error> {
313        (*value).try_into()
314    }
315}
316
317impl TryFrom<Word> for [bool; WORD_SIZE_FELT] {
318    type Error = WordError;
319
320    fn try_from(value: Word) -> Result<Self, Self::Error> {
321        fn to_bool(v: u64) -> Option<bool> {
322            if v <= 1 { Some(v == 1) } else { None }
323        }
324
325        Ok([
326            to_bool(value.0[0].as_int()).ok_or(WordError::TypeConversion("bool"))?,
327            to_bool(value.0[1].as_int()).ok_or(WordError::TypeConversion("bool"))?,
328            to_bool(value.0[2].as_int()).ok_or(WordError::TypeConversion("bool"))?,
329            to_bool(value.0[3].as_int()).ok_or(WordError::TypeConversion("bool"))?,
330        ])
331    }
332}
333
334impl TryFrom<&Word> for [u8; WORD_SIZE_FELT] {
335    type Error = WordError;
336
337    fn try_from(value: &Word) -> Result<Self, Self::Error> {
338        (*value).try_into()
339    }
340}
341
342impl TryFrom<Word> for [u8; WORD_SIZE_FELT] {
343    type Error = WordError;
344
345    fn try_from(value: Word) -> Result<Self, Self::Error> {
346        Ok([
347            value.0[0].as_int().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
348            value.0[1].as_int().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
349            value.0[2].as_int().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
350            value.0[3].as_int().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
351        ])
352    }
353}
354
355impl TryFrom<&Word> for [u16; WORD_SIZE_FELT] {
356    type Error = WordError;
357
358    fn try_from(value: &Word) -> Result<Self, Self::Error> {
359        (*value).try_into()
360    }
361}
362
363impl TryFrom<Word> for [u16; WORD_SIZE_FELT] {
364    type Error = WordError;
365
366    fn try_from(value: Word) -> Result<Self, Self::Error> {
367        Ok([
368            value.0[0].as_int().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
369            value.0[1].as_int().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
370            value.0[2].as_int().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
371            value.0[3].as_int().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
372        ])
373    }
374}
375
376impl TryFrom<&Word> for [u32; WORD_SIZE_FELT] {
377    type Error = WordError;
378
379    fn try_from(value: &Word) -> Result<Self, Self::Error> {
380        (*value).try_into()
381    }
382}
383
384impl TryFrom<Word> for [u32; WORD_SIZE_FELT] {
385    type Error = WordError;
386
387    fn try_from(value: Word) -> Result<Self, Self::Error> {
388        Ok([
389            value.0[0].as_int().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
390            value.0[1].as_int().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
391            value.0[2].as_int().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
392            value.0[3].as_int().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
393        ])
394    }
395}
396
397impl From<&Word> for [u64; WORD_SIZE_FELT] {
398    fn from(value: &Word) -> Self {
399        (*value).into()
400    }
401}
402
403impl From<Word> for [u64; WORD_SIZE_FELT] {
404    fn from(value: Word) -> Self {
405        [
406            value.0[0].as_int(),
407            value.0[1].as_int(),
408            value.0[2].as_int(),
409            value.0[3].as_int(),
410        ]
411    }
412}
413
414impl From<&Word> for [Felt; WORD_SIZE_FELT] {
415    fn from(value: &Word) -> Self {
416        (*value).into()
417    }
418}
419
420impl From<Word> for [Felt; WORD_SIZE_FELT] {
421    fn from(value: Word) -> Self {
422        value.0
423    }
424}
425
426impl From<&Word> for [u8; WORD_SIZE_BYTES] {
427    fn from(value: &Word) -> Self {
428        (*value).into()
429    }
430}
431
432impl From<Word> for [u8; WORD_SIZE_BYTES] {
433    fn from(value: Word) -> Self {
434        value.as_bytes()
435    }
436}
437
438impl From<&Word> for String {
439    /// The returned string starts with `0x`.
440    fn from(value: &Word) -> Self {
441        (*value).into()
442    }
443}
444
445impl From<Word> for String {
446    /// The returned string starts with `0x`.
447    fn from(value: Word) -> Self {
448        value.to_hex()
449    }
450}
451
452// CONVERSIONS: TO WORD
453// ================================================================================================
454
455impl From<&[bool; WORD_SIZE_FELT]> for Word {
456    fn from(value: &[bool; WORD_SIZE_FELT]) -> Self {
457        (*value).into()
458    }
459}
460
461impl From<[bool; WORD_SIZE_FELT]> for Word {
462    fn from(value: [bool; WORD_SIZE_FELT]) -> Self {
463        [value[0] as u32, value[1] as u32, value[2] as u32, value[3] as u32].into()
464    }
465}
466
467impl From<&[u8; WORD_SIZE_FELT]> for Word {
468    fn from(value: &[u8; WORD_SIZE_FELT]) -> Self {
469        (*value).into()
470    }
471}
472
473impl From<[u8; WORD_SIZE_FELT]> for Word {
474    fn from(value: [u8; WORD_SIZE_FELT]) -> Self {
475        Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()])
476    }
477}
478
479impl From<&[u16; WORD_SIZE_FELT]> for Word {
480    fn from(value: &[u16; WORD_SIZE_FELT]) -> Self {
481        (*value).into()
482    }
483}
484
485impl From<[u16; WORD_SIZE_FELT]> for Word {
486    fn from(value: [u16; WORD_SIZE_FELT]) -> Self {
487        Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()])
488    }
489}
490
491impl From<&[u32; WORD_SIZE_FELT]> for Word {
492    fn from(value: &[u32; WORD_SIZE_FELT]) -> Self {
493        (*value).into()
494    }
495}
496
497impl From<[u32; WORD_SIZE_FELT]> for Word {
498    fn from(value: [u32; WORD_SIZE_FELT]) -> Self {
499        Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()])
500    }
501}
502
503impl TryFrom<&[u64; WORD_SIZE_FELT]> for Word {
504    type Error = WordError;
505
506    fn try_from(value: &[u64; WORD_SIZE_FELT]) -> Result<Self, WordError> {
507        (*value).try_into()
508    }
509}
510
511impl TryFrom<[u64; WORD_SIZE_FELT]> for Word {
512    type Error = WordError;
513
514    fn try_from(value: [u64; WORD_SIZE_FELT]) -> Result<Self, WordError> {
515        Ok(Self([
516            value[0].try_into().map_err(WordError::InvalidFieldElement)?,
517            value[1].try_into().map_err(WordError::InvalidFieldElement)?,
518            value[2].try_into().map_err(WordError::InvalidFieldElement)?,
519            value[3].try_into().map_err(WordError::InvalidFieldElement)?,
520        ]))
521    }
522}
523
524impl From<&[Felt; WORD_SIZE_FELT]> for Word {
525    fn from(value: &[Felt; WORD_SIZE_FELT]) -> Self {
526        Self(*value)
527    }
528}
529
530impl From<[Felt; WORD_SIZE_FELT]> for Word {
531    fn from(value: [Felt; WORD_SIZE_FELT]) -> Self {
532        Self(value)
533    }
534}
535
536impl TryFrom<&[u8; WORD_SIZE_BYTES]> for Word {
537    type Error = WordError;
538
539    fn try_from(value: &[u8; WORD_SIZE_BYTES]) -> Result<Self, Self::Error> {
540        (*value).try_into()
541    }
542}
543
544impl TryFrom<[u8; WORD_SIZE_BYTES]> for Word {
545    type Error = WordError;
546
547    fn try_from(value: [u8; WORD_SIZE_BYTES]) -> Result<Self, Self::Error> {
548        // Note: the input length is known, the conversion from slice to array must succeed so the
549        // `unwrap`s below are safe
550        let a = u64::from_le_bytes(value[0..8].try_into().unwrap());
551        let b = u64::from_le_bytes(value[8..16].try_into().unwrap());
552        let c = u64::from_le_bytes(value[16..24].try_into().unwrap());
553        let d = u64::from_le_bytes(value[24..32].try_into().unwrap());
554
555        let a: Felt = a.try_into().map_err(WordError::InvalidFieldElement)?;
556        let b: Felt = b.try_into().map_err(WordError::InvalidFieldElement)?;
557        let c: Felt = c.try_into().map_err(WordError::InvalidFieldElement)?;
558        let d: Felt = d.try_into().map_err(WordError::InvalidFieldElement)?;
559
560        Ok(Word([a, b, c, d]))
561    }
562}
563
564impl TryFrom<&[u8]> for Word {
565    type Error = WordError;
566
567    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
568        let value: [u8; WORD_SIZE_BYTES] = value
569            .try_into()
570            .map_err(|_| WordError::InvalidInputLength("bytes", WORD_SIZE_BYTES, value.len()))?;
571        value.try_into()
572    }
573}
574
575impl TryFrom<&[Felt]> for Word {
576    type Error = WordError;
577
578    fn try_from(value: &[Felt]) -> Result<Self, Self::Error> {
579        let value: [Felt; WORD_SIZE_FELT] = value
580            .try_into()
581            .map_err(|_| WordError::InvalidInputLength("elements", WORD_SIZE_FELT, value.len()))?;
582        Ok(value.into())
583    }
584}
585
586impl TryFrom<&str> for Word {
587    type Error = WordError;
588
589    /// Expects the string to start with `0x`.
590    fn try_from(value: &str) -> Result<Self, Self::Error> {
591        hex_to_bytes::<WORD_SIZE_BYTES>(value)
592            .map_err(WordError::HexParse)
593            .and_then(Word::try_from)
594    }
595}
596
597impl TryFrom<String> for Word {
598    type Error = WordError;
599
600    /// Expects the string to start with `0x`.
601    fn try_from(value: String) -> Result<Self, Self::Error> {
602        value.as_str().try_into()
603    }
604}
605
606impl TryFrom<&String> for Word {
607    type Error = WordError;
608
609    /// Expects the string to start with `0x`.
610    fn try_from(value: &String) -> Result<Self, Self::Error> {
611        value.as_str().try_into()
612    }
613}
614
615// SERIALIZATION / DESERIALIZATION
616// ================================================================================================
617
618impl Serializable for Word {
619    fn write_into<W: ByteWriter>(&self, target: &mut W) {
620        target.write_bytes(&self.as_bytes());
621    }
622
623    fn get_size_hint(&self) -> usize {
624        Self::SERIALIZED_SIZE
625    }
626}
627
628impl Deserializable for Word {
629    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
630        let mut inner: [Felt; WORD_SIZE_FELT] = [ZERO; WORD_SIZE_FELT];
631        for inner in inner.iter_mut() {
632            let e = source.read_u64()?;
633            if e >= Felt::MODULUS {
634                return Err(DeserializationError::InvalidValue(String::from(
635                    "value not in the appropriate range",
636                )));
637            }
638            *inner = Felt::new(e);
639        }
640
641        Ok(Self(inner))
642    }
643}
644
645// ITERATORS
646// ================================================================================================
647impl IntoIterator for Word {
648    type Item = Felt;
649    type IntoIter = <[Felt; 4] as IntoIterator>::IntoIter;
650
651    fn into_iter(self) -> Self::IntoIter {
652        self.0.into_iter()
653    }
654}
655
656// MACROS
657// ================================================================================================
658
659/// Construct a new [Word](super::Word) from a hex value.
660///
661/// Expects a '0x' prefixed hex string followed by up to 64 hex digits.
662#[macro_export]
663macro_rules! word {
664    ($hex:expr) => {{
665        let word: Word = match $crate::word::Word::parse($hex) {
666            Ok(v) => v,
667            Err(e) => panic!("{}", e),
668        };
669
670        word
671    }};
672}