Skip to main content

pathfinder_crypto/algebra/field/
felt.rs

1use std::borrow::Cow;
2use std::error::Error;
3use std::str::FromStr;
4
5use bitvec::order::Msb0;
6use bitvec::slice::BitSlice;
7use bitvec::view::BitView;
8use fake::Dummy;
9use num_bigint::{BigUint, ParseBigIntError};
10
11use crate::algebra::field::montfelt::MontFelt;
12
13/// Starknet Field Element.
14///
15/// A field element is a number 0..p-1 with p=2^{251}+17*2^{192}+1, and it forms
16/// the basic building block of most Starknet interactions.
17#[derive(Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
18pub struct Felt([u8; 32]);
19
20const MODULUS_U64: [u64; 4] = [576460752303423505u64, 0, 0, 1];
21
22impl std::fmt::Debug for Felt {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        write!(f, "Felt({self})")
25    }
26}
27
28impl std::fmt::Display for Felt {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        // 0xABCDEF1234567890
31        write!(f, "0x{self:X}")
32    }
33}
34
35impl std::fmt::LowerHex for Felt {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        self.0.iter().try_for_each(|&b| write!(f, "{b:02x}"))
38    }
39}
40
41impl std::fmt::UpperHex for Felt {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        self.0.iter().try_for_each(|&b| write!(f, "{b:02X}"))
44    }
45}
46
47impl Default for Felt {
48    fn default() -> Self {
49        Felt::ZERO
50    }
51}
52
53impl<T> Dummy<T> for Felt {
54    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &T, rng: &mut R) -> Self {
55        let mut bytes = [0u8; 32];
56        rng.fill_bytes(&mut bytes);
57        // Some 252 bit values are fine too but we don't really care here
58        bytes[0] &= 0x03;
59        Self(bytes)
60    }
61}
62
63/// Error returned by [Felt::from_be_bytes] indicating the maximum field value
64/// was exceeded.
65#[derive(Debug, PartialEq, Eq, Clone, Copy)]
66pub struct OverflowError;
67
68impl Error for OverflowError {}
69
70const OVERFLOW_MSG: &str = "The maximum field value was exceeded.";
71
72impl std::fmt::Display for OverflowError {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        f.write_str(OVERFLOW_MSG)
75    }
76}
77
78impl Felt {
79    pub const ZERO: Felt = Felt([0u8; 32]);
80    pub const ONE: Felt = Self::from_u64(1);
81
82    /// Return true if the element is zero.
83    pub fn is_zero(&self) -> bool {
84        self == &Felt::ZERO
85    }
86
87    /// Returns the big-endian representation of this [Felt].
88    pub const fn to_be_bytes(self) -> [u8; 32] {
89        self.0
90    }
91
92    /// Returns the little-endian representation of this [Felt].
93    pub fn to_le_bytes(self) -> [u8; 32] {
94        let mut tmp = self.0;
95        tmp.reverse();
96        tmp
97    }
98
99    /// Big-endian representation of this [Felt].
100    pub const fn as_be_bytes(&self) -> &[u8; 32] {
101        &self.0
102    }
103
104    /// Big-endian mutable representation of this [Felt].
105    pub fn as_mut_be_bytes(&mut self) -> &mut [u8; 32] {
106        &mut self.0
107    }
108
109    /// Convenience function which extends [Felt::from_be_bytes] to work with
110    /// slices.
111    pub const fn from_be_slice(bytes: &[u8]) -> Result<Self, OverflowError> {
112        if bytes.len() > 32 {
113            return Err(OverflowError);
114        }
115
116        let mut buf = [0u8; 32];
117        let mut index = 0;
118        loop {
119            if index == bytes.len() {
120                break;
121            }
122            buf[32 - bytes.len() + index] = bytes[index];
123            index += 1;
124        }
125
126        Felt::from_be_bytes(buf)
127    }
128
129    pub fn random<R: rand::Rng>(rng: &mut R) -> Self {
130        let r = MontFelt::random(rng);
131        Felt::from(r)
132    }
133
134    /// Creates a [Felt] from big-endian bytes.
135    ///
136    /// Returns [OverflowError] if not less than the field modulus.
137    pub const fn from_be_bytes(bytes: [u8; 32]) -> Result<Self, OverflowError> {
138        #[rustfmt::skip]
139        let limbs = [
140            u64::from_be_bytes([
141                bytes[0], bytes[1], bytes[2], bytes[3],
142                bytes[4], bytes[5], bytes[6], bytes[7],
143            ]),
144            u64::from_be_bytes([
145                bytes[8], bytes[9], bytes[10], bytes[11],
146                bytes[12], bytes[13], bytes[14], bytes[15],
147            ]),
148            u64::from_be_bytes([
149                bytes[16], bytes[17], bytes[18], bytes[19],
150                bytes[20], bytes[21], bytes[22], bytes[23],
151            ]),
152            u64::from_be_bytes([
153                bytes[24], bytes[25], bytes[26], bytes[27],
154                bytes[28], bytes[29], bytes[30], bytes[31],
155            ]),
156        ];
157
158        // Loop over each word, if all previous are equal and current is less, we are
159        // good.
160        let mut maybe_overflow = true;
161        let mut i = 0;
162        while i < 4 && maybe_overflow {
163            if limbs[i] < MODULUS_U64[i] {
164                maybe_overflow = false;
165            } else if limbs[i] > MODULUS_U64[i] {
166                return Err(OverflowError);
167            }
168            i += 1;
169        }
170        if maybe_overflow {
171            Err(OverflowError)
172        } else {
173            Ok(Felt(bytes))
174        }
175    }
176
177    /// Returns a bit view of the 251 least significant bits in MSB order.
178    pub fn view_bits(&self) -> &BitSlice<u8, Msb0> {
179        &self.0.view_bits()[5..]
180    }
181
182    /// Creates a [Felt] from up-to 251 bits.
183    pub fn from_bits(bits: &BitSlice<u8, Msb0>) -> Result<Self, OverflowError> {
184        if bits.len() > 251 {
185            return Err(OverflowError);
186        }
187
188        let mut bytes = [0u8; 32];
189        bytes.view_bits_mut::<Msb0>()[256 - bits.len()..].copy_from_bitslice(bits);
190
191        Ok(Self(bytes))
192    }
193
194    /// Returns `true` if the value of [`Felt`] is larger than `2^251 - 1`.
195    ///
196    /// Every [`Felt`] that is used to traverse a Merkle-Patricia Tree
197    /// must not exceed 251 bits, since 251 is the height of the tree.
198    pub const fn has_more_than_251_bits(&self) -> bool {
199        self.0[0] & 0b1111_1000 > 0
200    }
201
202    pub const fn from_u64(u: u64) -> Self {
203        const_expect!(
204            Self::from_be_slice(&u.to_be_bytes()),
205            "64 bits is less than 251 bits"
206        )
207    }
208
209    pub const fn from_u128(u: u128) -> Self {
210        const_expect!(
211            Self::from_be_slice(&u.to_be_bytes()),
212            "128 bits is less than 251 bits"
213        )
214    }
215}
216
217macro_rules! const_expect {
218    ($e:expr, $why:expr) => {{
219        match $e {
220            Ok(x) => x,
221            Err(_) => panic!(concat!("Expectation failed: ", $why)),
222        }
223    }};
224}
225
226use const_expect;
227
228use crate::algebra::field::CurveOrderMontFelt;
229
230impl From<u64> for Felt {
231    fn from(value: u64) -> Self {
232        Self::from_u64(value)
233    }
234}
235
236impl From<usize> for Felt {
237    fn from(value: usize) -> Self {
238        Self::from_u64(value.try_into().expect("ptr size is 64 bits"))
239    }
240}
241
242impl From<u128> for Felt {
243    fn from(value: u128) -> Self {
244        Self::from_u128(value)
245    }
246}
247
248impl From<[u8; 32]> for Felt {
249    fn from(bytes: [u8; 32]) -> Self {
250        Self(bytes)
251    }
252}
253
254impl TryInto<u128> for Felt {
255    type Error = OverflowError;
256
257    fn try_into(self) -> Result<u128, Self::Error> {
258        let initial_zeroes = self.0.iter().take_while(|b| **b == 0).count();
259        const EXPECTED_ZEROES: usize = (32 - u128::BITS / u8::BITS) as usize;
260
261        if initial_zeroes < EXPECTED_ZEROES {
262            return Err(OverflowError);
263        }
264
265        let bytes = self.0[EXPECTED_ZEROES..]
266            .try_into()
267            .expect("Should match u128 size");
268        Ok(u128::from_be_bytes(bytes))
269    }
270}
271
272impl TryInto<u64> for Felt {
273    type Error = OverflowError;
274
275    fn try_into(self) -> Result<u64, Self::Error> {
276        let initial_zeroes = self.0.iter().take_while(|b| **b == 0).count();
277        const EXPECTED_ZEROES: usize = (32 - u64::BITS / u8::BITS) as usize;
278
279        if initial_zeroes < EXPECTED_ZEROES {
280            return Err(OverflowError);
281        }
282        let bytes = self.0[EXPECTED_ZEROES..]
283            .try_into()
284            .expect("Should match u64 size");
285        Ok(u64::from_be_bytes(bytes))
286    }
287}
288
289impl std::ops::Add for Felt {
290    type Output = Felt;
291
292    fn add(self, rhs: Self) -> Self::Output {
293        let result = MontFelt::from(self) + MontFelt::from(rhs);
294        Felt::from(result)
295    }
296}
297
298impl std::ops::Sub for Felt {
299    type Output = Felt;
300
301    fn sub(self, rhs: Self) -> Self::Output {
302        let result = MontFelt::from(self) - MontFelt::from(rhs);
303        Felt::from(result)
304    }
305}
306
307impl From<MontFelt> for Felt {
308    fn from(fp: MontFelt) -> Self {
309        // safe as BE-bytes representations are the same
310        debug_assert_eq!(std::mem::size_of::<MontFelt>(), std::mem::size_of::<Felt>());
311        let bytes: [u8; 32] = fp.to_be_bytes();
312        Felt::from_be_bytes(bytes).unwrap()
313    }
314}
315
316impl From<CurveOrderMontFelt> for Felt {
317    fn from(value: CurveOrderMontFelt) -> Self {
318        // safe as BE-bytes representations are the same
319        debug_assert_eq!(std::mem::size_of::<MontFelt>(), std::mem::size_of::<Felt>());
320        let bytes = value.to_be_bytes();
321        Felt::from_be_bytes(bytes).unwrap()
322    }
323}
324
325impl Felt {
326    /// A convenience function which parses a hex string into a [Felt].
327    ///
328    /// Supports both upper and lower case hex strings. The '0x' prefix is
329    /// mandatory.
330    pub const fn from_hex_str(hex_str: &str) -> Result<Self, HexParseError> {
331        const fn parse_hex_digit(digit: u8) -> Result<u8, HexParseError> {
332            match digit {
333                b'0'..=b'9' => Ok(digit - b'0'),
334                b'A'..=b'F' => Ok(digit - b'A' + 10),
335                b'a'..=b'f' => Ok(digit - b'a' + 10),
336                other => Err(HexParseError::InvalidNibble(other)),
337            }
338        }
339
340        let bytes = hex_str.as_bytes();
341        let len = match bytes {
342            [b'0', b'x', rest @ ..] if rest.len() > 64 => {
343                return Err(HexParseError::InvalidLength {
344                    max: 64,
345                    actual: rest.len(),
346                })
347            }
348            [b'0', b'x', rest @ ..] => rest.len(),
349            _ => return Err(HexParseError::MissingPrefix),
350        };
351
352        let mut buf = [0u8; 32];
353
354        // We want the result in big-endian so reverse iterate over each pair of
355        // nibbles. let chunks = hex_str.as_bytes().rchunks_exact(2);
356
357        // Handle a possible odd nibble remaining nibble.
358        if len % 2 == 1 {
359            let idx = len / 2;
360            buf[31 - idx] = match parse_hex_digit(bytes[2]) {
361                Ok(b) => b,
362                Err(e) => return Err(e),
363            };
364        }
365
366        let chunks = len / 2;
367        let mut chunk = 0;
368
369        while chunk < chunks {
370            let lower = match parse_hex_digit(bytes[bytes.len() - chunk * 2 - 1]) {
371                Ok(b) => b,
372                Err(e) => return Err(e),
373            };
374            let upper = match parse_hex_digit(bytes[bytes.len() - chunk * 2 - 2]) {
375                Ok(b) => b,
376                Err(e) => return Err(e),
377            };
378            buf[31 - chunk] = (upper << 4) | lower;
379            chunk += 1;
380        }
381
382        let felt = match Felt::from_be_bytes(buf) {
383            Ok(felt) => felt,
384            Err(OverflowError) => return Err(HexParseError::Overflow),
385        };
386        Ok(felt)
387    }
388
389    /// The first stage of conversion - skip leading zeros
390    fn skip_zeros(&self) -> (impl Iterator<Item = &u8>, usize, usize) {
391        // Skip all leading zero bytes
392        let it = self.0.iter().skip_while(|&&b| b == 0);
393        let num_bytes = it.clone().count();
394        let skipped = self.0.len() - num_bytes;
395        // The first high nibble can be 0
396        let start = if self.0[skipped] < 0x10 { 1 } else { 2 };
397        // Number of characters to display
398        let len = start + num_bytes * 2;
399        (it, start, len)
400    }
401
402    /// The second stage of conversion - map bytes to hex str
403    fn it_to_hex_str<'a>(
404        it: impl Iterator<Item = &'a u8>,
405        start: usize,
406        len: usize,
407        buf: &'a mut [u8],
408    ) -> &'a [u8] {
409        const LUT: [u8; 16] = *b"0123456789abcdef";
410        buf[0] = b'0';
411        // Same small lookup table is ~25% faster than hex::encode_from_slice 🤷
412        it.enumerate().for_each(|(i, &b)| {
413            let idx = b as usize;
414            let pos = start + i * 2;
415            let x = [LUT[(idx & 0xf0) >> 4], LUT[idx & 0x0f]];
416            buf[pos..pos + 2].copy_from_slice(&x);
417        });
418        buf[1] = b'x';
419        &buf[..len]
420    }
421
422    /// A convenience function which produces a "0x" prefixed hex str slice in a
423    /// given buffer `buf` from a [Felt].
424    /// Panics if `self.0.len() * 2 + 2 > buf.len()`
425    pub fn as_hex_str<'a>(&'a self, buf: &'a mut [u8]) -> &'a str {
426        let expected_buf_len = self.0.len() * 2 + 2;
427        assert!(
428            buf.len() >= expected_buf_len,
429            "buffer size is {}, expected at least {}",
430            buf.len(),
431            expected_buf_len
432        );
433
434        if !self.0.iter().any(|b| *b != 0) {
435            return "0x0";
436        }
437
438        let (it, start, len) = self.skip_zeros();
439        let res = Self::it_to_hex_str(it, start, len, buf);
440        // Unwrap is safe because `buf` holds valid UTF8 characters.
441        std::str::from_utf8(res).unwrap()
442    }
443
444    /// A convenience function which produces a "0x" prefixed hex string from a
445    /// [Felt].
446    pub fn to_hex_str(&self) -> Cow<'static, str> {
447        if !self.0.iter().any(|b| *b != 0) {
448            return Cow::from("0x0");
449        }
450        let (it, start, len) = self.skip_zeros();
451        let mut buf = vec![0u8; len];
452        Self::it_to_hex_str(it, start, len, &mut buf);
453        // Unwrap is safe as the buffer contains valid utf8
454        String::from_utf8(buf).unwrap().into()
455    }
456
457    /// Creates a [Felt] from a [BigUint].
458    ///
459    /// A helper conversion function. Only use with __sequencer API related
460    /// types__.
461    pub fn from_biguint(b: BigUint) -> Result<Self, OverflowError> {
462        Self::from_be_slice(&b.to_bytes_be())
463    }
464
465    /// Parses a decimal string into a [Felt].
466    ///
467    /// A helper conversion function. Only use with __sequencer API related
468    /// types__.
469    pub fn from_dec_str(s: &str) -> Result<Self, DecParseError> {
470        // The order here matters because `Felt::from_hex_str` requires the '0x'
471        // prefix, so we'll never parse a hex string as a decimal string by mistake.
472        match Felt::from_hex_str(s) {
473            Ok(h) => Ok(h),
474            Err(_) => {
475                let b = BigUint::from_str(s)?;
476                let h = Self::from_biguint(b)?;
477                Ok(h)
478            }
479        }
480    }
481
482    /// Converts this [Felt] into a decimal string.
483    ///
484    /// A helper conversion function. Only use with __sequencer API related
485    /// types__.
486    pub fn to_dec_str(&self) -> String {
487        let b = self.to_be_bytes();
488        let b = BigUint::from_bytes_be(&b);
489        b.to_str_radix(10)
490    }
491}
492
493/// Error returned by [Felt::from_hex_str] indicating an invalid hex string.
494#[derive(Debug, PartialEq, Eq)]
495pub enum HexParseError {
496    InvalidNibble(u8),
497    InvalidLength { max: usize, actual: usize },
498    MissingPrefix,
499    Overflow,
500}
501
502impl Error for HexParseError {}
503
504impl From<OverflowError> for HexParseError {
505    fn from(_: OverflowError) -> Self {
506        Self::Overflow
507    }
508}
509
510impl std::fmt::Display for HexParseError {
511    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
512        match self {
513            Self::InvalidNibble(n) => f.write_fmt(format_args!("Invalid nibble found: 0x{:x}", *n)),
514            Self::InvalidLength { max, actual } => {
515                f.write_fmt(format_args!("More than {} digits found: {}", *max, *actual))
516            }
517            Self::MissingPrefix => f.write_str("Missing '0x' prefix"),
518            Self::Overflow => f.write_str(OVERFLOW_MSG),
519        }
520    }
521}
522
523/// An error type for [`Felt::from_dec_str`] indicating an invalid decimal
524/// string or an overflow.
525#[derive(Debug)]
526pub enum DecParseError {
527    ParseBigInt(ParseBigIntError),
528    Overflow,
529}
530
531impl Error for DecParseError {}
532
533impl From<ParseBigIntError> for DecParseError {
534    fn from(e: ParseBigIntError) -> Self {
535        Self::ParseBigInt(e)
536    }
537}
538
539impl From<OverflowError> for DecParseError {
540    fn from(_: OverflowError) -> Self {
541        Self::Overflow
542    }
543}
544
545impl std::fmt::Display for DecParseError {
546    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
547        match self {
548            Self::ParseBigInt(e) => {
549                f.write_fmt(format_args!("Failed to parse decimal string: {e}"))
550            }
551            Self::Overflow => f.write_str(OVERFLOW_MSG),
552        }
553    }
554}
555
556#[cfg(test)]
557mod tests {
558    use bitvec::bitvec;
559    use pretty_assertions_sorted::assert_eq;
560
561    use super::*;
562
563    const MODULUS_U8: [u8; 32] = [
564        8, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
565        0, 1,
566    ];
567
568    #[test]
569    fn view_bits() {
570        let one = Felt::from_hex_str("0x1").unwrap();
571
572        let one = one.view_bits().to_bitvec();
573
574        let mut expected = bitvec![0; 251];
575        expected.set(250, true);
576        assert_eq!(one, expected);
577    }
578
579    #[test]
580    fn bits_round_trip() {
581        let mut bits = bitvec![u8, Msb0; 1; 251];
582        bits.set(0, false);
583        bits.set(1, false);
584        bits.set(2, false);
585        bits.set(3, false);
586        bits.set(4, false);
587
588        let res = Felt::from_bits(&bits).unwrap();
589
590        let x = res.view_bits();
591        let y = Felt::from_bits(x).unwrap();
592
593        assert_eq!(res, y);
594    }
595
596    #[test]
597    fn bytes_round_trip() {
598        let original = [
599            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
600            0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
601            0x1C, 0x1D, 0x1E, 0x1F,
602        ];
603        let hash = Felt::from_be_bytes(original).unwrap();
604        let bytes = hash.to_be_bytes();
605        assert_eq!(bytes, original);
606    }
607
608    #[test]
609    fn from_bytes_overflow() {
610        // Field modulus
611        assert_eq!(Felt::from_be_bytes(MODULUS_U8), Err(OverflowError));
612        // Field modulus - 1
613        let mut max_val = MODULUS_U8;
614        max_val[31] -= 1;
615        Felt::from_be_bytes(max_val).unwrap();
616    }
617
618    #[test]
619    fn field_round_trip() {
620        let bytes = [
621            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
622            0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
623            0x1C, 0x1D, 0x1E, 0x1F,
624        ];
625        let original = Felt::from_be_bytes(bytes).unwrap();
626        let fp = MontFelt::from(original);
627        let hash = Felt::from(fp);
628        assert_eq!(hash, original);
629    }
630
631    mod from_be_slice {
632        use pretty_assertions_sorted::assert_eq;
633
634        use super::*;
635
636        #[test]
637        fn round_trip() {
638            let original = Felt::from_hex_str("0xabcdef0123456789").unwrap();
639            let bytes = original.to_be_bytes();
640            let result = Felt::from_be_slice(&bytes[..]).unwrap();
641
642            assert_eq!(result, original);
643        }
644
645        #[test]
646        fn too_long() {
647            let original = Felt::from_hex_str("0xabcdef0123456789").unwrap();
648            let mut bytes = original.to_be_bytes().to_vec();
649            bytes.push(0);
650            Felt::from_be_slice(&bytes[..]).unwrap_err();
651        }
652
653        #[test]
654        fn short_slice() {
655            let original = Felt::from_hex_str("0xabcdef0123456789").unwrap();
656            let bytes = original.to_be_bytes();
657            let result = Felt::from_be_slice(&bytes[24..]);
658
659            assert_eq!(result, Ok(original));
660        }
661
662        #[test]
663        fn max() {
664            let mut max_val = MODULUS_U8;
665            max_val[31] -= 1;
666            Felt::from_be_slice(&max_val[..]).unwrap();
667        }
668
669        #[test]
670        fn overflow() {
671            assert_eq!(Felt::from_be_slice(&MODULUS_U8[..]), Err(OverflowError));
672        }
673    }
674
675    mod fmt {
676        use pretty_assertions_sorted::assert_eq;
677
678        use super::Felt;
679
680        #[test]
681        fn debug() {
682            let hex_str = "0x1234567890abcdef000edcba0987654321";
683            let felt = Felt::from_hex_str(hex_str).unwrap();
684            let result = format!("{felt:?}");
685
686            let leading_zeros = "0".repeat(64 + 2 - hex_str.len());
687            let hex_upper_stripped = hex_str.strip_prefix("0x").unwrap().to_uppercase();
688            let expected = format!("Felt(0x{leading_zeros}{hex_upper_stripped})");
689
690            assert_eq!(result, expected);
691        }
692
693        #[test]
694        fn fmt() {
695            let hex_str = "0x1234567890abcdef000edcba0987654321";
696            let starkhash = Felt::from_hex_str(hex_str).unwrap();
697            let result = format!("{starkhash:x}");
698
699            let mut expected = "0".repeat(64 - hex_str.len() + 2);
700            expected.push_str(&hex_str[2..]);
701
702            // We don't really care which casing is used by fmt.
703            assert_eq!(result.to_lowercase(), expected.to_lowercase());
704        }
705
706        #[test]
707        fn lower_hex() {
708            let hex_str = "0x1234567890abcdef000edcba0987654321";
709            let starkhash = Felt::from_hex_str(hex_str).unwrap();
710            let result = format!("{starkhash:x}");
711
712            let mut expected = "0".repeat(64 - hex_str.len() + 2);
713            expected.push_str(&hex_str[2..]);
714
715            assert_eq!(result, expected.to_lowercase());
716        }
717
718        #[test]
719        fn upper_hex() {
720            let hex_str = "0x1234567890abcdef000edcba0987654321";
721            let starkhash = Felt::from_hex_str(hex_str).unwrap();
722            let result = format!("{starkhash:X}");
723
724            let mut expected = "0".repeat(64 - hex_str.len() + 2);
725            expected.push_str(&hex_str[2..]);
726
727            assert_eq!(result, expected.to_uppercase());
728        }
729    }
730
731    mod from_hex_str {
732        use assert_matches::assert_matches;
733        use pretty_assertions_sorted::assert_eq;
734
735        use super::*;
736
737        /// Test hex string with its expected [Felt].
738        fn test_data() -> (&'static str, Felt) {
739            let mut expected = [0; 32];
740            expected[31] = 0xEF;
741            expected[30] = 0xCD;
742            expected[29] = 0xAB;
743            expected[28] = 0xef;
744            expected[27] = 0xcd;
745            expected[26] = 0xab;
746            expected[25] = 0x89;
747            expected[24] = 0x67;
748            expected[23] = 0x45;
749            expected[22] = 0x23;
750            expected[21] = 0x01;
751            let expected = Felt::from_be_bytes(expected).unwrap();
752
753            ("0123456789abcdefABCDEF", expected)
754        }
755
756        #[test]
757        fn simple() {
758            let (test_str, expected) = test_data();
759            let uut = Felt::from_hex_str(&format!("0x{test_str}")).unwrap();
760            assert_eq!(uut, expected);
761        }
762
763        #[test]
764        fn leading_zeros() {
765            let (test_str, expected) = test_data();
766            let uut = Felt::from_hex_str(&format!("0x000000000{test_str}")).unwrap();
767            assert_eq!(uut, expected);
768        }
769
770        #[test]
771        fn invalid_nibble() {
772            assert_matches!(Felt::from_hex_str("0x123z").unwrap_err(), HexParseError::InvalidNibble(n) => assert_eq!(n, b'z'))
773        }
774
775        #[test]
776        fn missing_prefix() {
777            assert_matches!(
778                Felt::from_hex_str("123").unwrap_err(),
779                HexParseError::MissingPrefix
780            )
781        }
782
783        #[test]
784        fn invalid_len() {
785            let invalid_str = "0".repeat(65);
786            assert_matches!(
787                Felt::from_hex_str(&format!("0x{invalid_str}")).unwrap_err(),
788                HexParseError::InvalidLength {
789                    max: 64,
790                    actual: 65
791                }
792            );
793        }
794
795        #[test]
796        fn overflow() {
797            // Field modulus
798            let mut modulus =
799                "0x800000000000011000000000000000000000000000000000000000000000001".to_string();
800            assert_eq!(
801                Felt::from_hex_str(&modulus).unwrap_err(),
802                HexParseError::Overflow
803            );
804            // Field modulus - 1
805            modulus.pop();
806            modulus.push('0');
807            Felt::from_hex_str(&modulus).unwrap();
808        }
809    }
810
811    mod to_hex_str {
812        use pretty_assertions_sorted::assert_eq;
813
814        use super::*;
815
816        const ODD: &str = "0x1234567890abcde";
817        const EVEN: &str = "0x1234567890abcdef";
818        const MAX: &str = "0x800000000000011000000000000000000000000000000000000000000000000";
819
820        #[test]
821        fn zero() {
822            assert_eq!(Felt::ZERO.to_hex_str(), "0x0");
823            let mut buf = [0u8; 66];
824            assert_eq!(Felt::ZERO.as_hex_str(&mut buf), "0x0");
825        }
826
827        #[test]
828        fn odd() {
829            let hash = Felt::from_hex_str(ODD).unwrap();
830            assert_eq!(hash.to_hex_str(), ODD);
831            let mut buf = [0u8; 66];
832            assert_eq!(hash.as_hex_str(&mut buf), ODD);
833        }
834
835        #[test]
836        fn even() {
837            let hash = Felt::from_hex_str(EVEN).unwrap();
838            assert_eq!(hash.to_hex_str(), EVEN);
839            let mut buf = [0u8; 66];
840            assert_eq!(hash.as_hex_str(&mut buf), EVEN);
841        }
842
843        #[test]
844        fn max() {
845            let hash = Felt::from_hex_str(MAX).unwrap();
846            assert_eq!(hash.to_hex_str(), MAX);
847            let mut buf = [0u8; 66];
848            assert_eq!(hash.as_hex_str(&mut buf), MAX);
849        }
850
851        #[test]
852        #[should_panic]
853        fn buffer_too_small() {
854            let mut buf = [0u8; 65];
855            Felt::ZERO.as_hex_str(&mut buf);
856        }
857    }
858
859    mod has_more_than_251_bits {
860        use super::*;
861
862        #[test]
863        fn has_251_bits() {
864            let mut bytes = [0xFFu8; 32];
865            bytes[0] = 0x07;
866            let h = Felt::from_be_bytes(bytes).unwrap();
867            assert!(!h.has_more_than_251_bits());
868        }
869
870        #[test]
871        fn has_252_bits() {
872            let mut bytes = [0u8; 32];
873            bytes[0] = 0x08;
874            let h = Felt::from_be_bytes(bytes).unwrap();
875            assert!(h.has_more_than_251_bits());
876        }
877    }
878}