Skip to main content

pathfinder_common/
macros.rs

1/// Macros for newtypes stored with an sqlite INTEGER column.
2pub(super) mod i64_backed_u64 {
3
4    /// Generates `new`, `new_or_panic` and `get` methods, `PartialEq` against
5    /// `i64` and `u64`, and `fake::Dummy`.
6    macro_rules! new_get_partialeq {
7        ($target:ty) => {
8            impl $target {
9                pub const fn new(val: u64) -> Option<Self> {
10                    let max = i64::MAX as u64;
11                    // Range::contains is not const
12                    if val <= max {
13                        Some(Self(val))
14                    } else {
15                        None
16                    }
17                }
18
19                pub const fn new_or_panic(val: u64) -> Self {
20                    match Self::new(val) {
21                        Some(x) => x,
22                        None => panic!("Invalid constant"),
23                    }
24                }
25
26                pub const fn get(&self) -> u64 {
27                    self.0
28                }
29            }
30
31            impl PartialEq<u64> for $target {
32                fn eq(&self, other: &u64) -> bool {
33                    self.0 == *other
34                }
35            }
36
37            impl PartialEq<i64> for $target {
38                fn eq(&self, other: &i64) -> bool {
39                    u64::try_from(*other).map(|x| self == &x).unwrap_or(false)
40                }
41            }
42
43            impl PartialEq<u32> for $target {
44                fn eq(&self, other: &u32) -> bool {
45                    self.0 == *other as u64
46                }
47            }
48
49            impl PartialEq<i32> for $target {
50                fn eq(&self, other: &i32) -> bool {
51                    u64::try_from(*other).map(|x| self == &x).unwrap_or(false)
52                }
53            }
54
55            impl<T> fake::Dummy<T> for $target {
56                fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &T, rng: &mut R) -> Self {
57                    Self(rng.gen_range(0..i64::MAX as u64))
58                }
59            }
60        };
61    }
62
63    /// Generates a u64 alike serialization and deserialization.
64    macro_rules! serdes {
65        ($target:ty) => {
66            impl serde::Serialize for $target {
67                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
68                where
69                    S: serde::Serializer,
70                {
71                    serializer.serialize_u64(self.0)
72                }
73            }
74
75            impl<'de> serde::Deserialize<'de> for $target {
76                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
77                where
78                    D: serde::Deserializer<'de>,
79                {
80                    let raw = u64::deserialize(deserializer)?;
81                    <$target>::deserialize_value::<D::Error>(raw)
82                }
83            }
84
85            impl $target {
86                pub fn deserialize_value<E>(raw: u64) -> Result<Self, E>
87                where
88                    E: serde::de::Error,
89                {
90                    <$target>::new(raw).ok_or_else(|| {
91                        serde::de::Error::invalid_value(
92                            serde::de::Unexpected::Unsigned(raw),
93                            &"i64::MAX unsigned integer",
94                        )
95                    })
96                }
97            }
98        };
99    }
100
101    pub(crate) use new_get_partialeq;
102    pub(crate) use serdes;
103}
104
105/// Generates felt newtype-wrappers and the `macro_prelude` module.
106///
107/// Note that this is a single-use macro as it generates a module.
108///
109/// Usage:
110///     `felt_newtypes!([x1, x2, ..]; [y1, y2, ..])`
111/// where `x` is the set of `Felt` wrapper types and `y` the `Felt251` wrappers.
112macro_rules! felt_newtypes {
113    ([$($felt:ident),* $(,)?]; [$($felt251:ident),* $(,)?]) => {
114        crate::macros::felt_newtypes!(@define_felt $($felt),*);
115        crate::macros::felt_newtypes!(@define_felt251 $($felt251),*);
116
117        pub mod macro_prelude {
118            pub use super::felt;
119            pub use super::felt_bytes;
120
121            crate::macros::felt_newtypes!(@generate_felt_macro $($felt),*);
122            crate::macros::felt_newtypes!(@generate_felt251_macro $($felt251),*);
123
124            crate::macros::felt_newtypes!(@generate_use $($felt),*);
125            crate::macros::felt_newtypes!(@generate_use $($felt251),*);
126        }
127    };
128
129    (@define_felt $head:ident, $($tail:ident),+ $(,)?) => {
130        crate::macros::felt_newtypes!(@define_felt $head);
131        crate::macros::felt_newtypes!(@define_felt $($tail),+);
132    };
133
134    (@define_felt $target:ident) => {
135        paste::paste! {
136            #[derive(Copy, Clone, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, PartialOrd, Ord, Dummy)]
137            pub struct $target(pub pathfinder_crypto::Felt);
138
139            #[allow(unused)]
140            impl $target {
141                pub const ZERO: Self = Self(pathfinder_crypto::Felt::ZERO);
142
143                pub fn as_inner(&self) -> &pathfinder_crypto::Felt {
144                    &self.0
145                }
146            }
147
148            $crate::macros::fmt::thin_debug!($target);
149            $crate::macros::fmt::thin_display!($target);
150        }
151    };
152
153    (@define_felt251 $head:ident, $($tail:ident),+ $(,)?) => {
154        crate::macros::felt_newtypes!(@define_felt251 $head);
155        crate::macros::felt_newtypes!(@define_felt251 $($tail),+);
156    };
157
158    (@define_felt251 $target:ident) => {
159        paste::paste! {
160            #[derive(Copy, Clone, Default, PartialEq, Eq, Hash, serde::Serialize, PartialOrd, Ord, Dummy)]
161            pub struct $target(pub pathfinder_crypto::Felt);
162
163            $crate::macros::fmt::thin_debug!($target);
164            $crate::macros::fmt::thin_display!($target);
165
166            impl $target {
167                pub const ZERO: Self = Self(pathfinder_crypto::Felt::ZERO);
168
169                pub fn as_inner(&self) -> &pathfinder_crypto::Felt {
170                    &self.0
171                }
172
173                pub const fn new(hash: Felt) -> Option<Self> {
174                    if hash.has_more_than_251_bits() {
175                        None
176                    } else {
177                        Some(Self(hash))
178                    }
179                }
180
181                pub const fn new_or_panic(hash: Felt) -> Self {
182                    match Self::new(hash) {
183                        Some(key) => key,
184                        None => panic!("Too many bits, need less for MPT keys"),
185                    }
186                }
187
188                pub const fn get(&self) -> &Felt {
189                    &self.0
190                }
191
192                pub fn view_bits(&self) -> &bitvec::slice::BitSlice<u8, bitvec::order::Msb0> {
193                    self.0.view_bits()
194                }
195            }
196
197            impl<'de> serde::Deserialize<'de> for $target {
198                fn deserialize<D>(de: D) -> Result<Self, D::Error>
199                where
200                    D: serde::Deserializer<'de>,
201                {
202                    let felt = Felt::deserialize(de)?;
203                    $target::new(felt).context("Felt251 overflow").map_err(serde::de::Error::custom)
204                }
205            }
206        }
207    };
208
209    (@generate_use $head:ident, $($tail:ident),+ $(,)?) => {
210        crate::macros::felt_newtypes!(@generate_use $head);
211        crate::macros::felt_newtypes!(@generate_use $($tail),+);
212    };
213
214    (@generate_use $target:ident) => {
215        paste::paste! {
216            pub use [<$target:snake>];
217            pub use [<$target:snake _bytes>];
218        }
219    };
220
221    (@generate_felt_macro $head:ident, $($tail:ident),+ $(,)?) => {
222        crate::macros::felt_newtypes!(@generate_felt_macro $head);
223        crate::macros::felt_newtypes!(@generate_felt_macro $($tail),+);
224    };
225
226    (@generate_felt_macro $target:ident) => {
227        paste::paste! {
228            #[macro_export]
229            macro_rules! [<$target:snake>] {
230                ($hex:expr) => {
231                    $crate::$target($crate::felt!($hex))
232                };
233            }
234
235            #[macro_export]
236            macro_rules! [<$target:snake _bytes>] {
237                ($bytes:expr) => {
238                    $crate::$target($crate::felt_bytes!($bytes))
239                };
240            }
241        }
242    };
243
244    (@generate_felt251_macro $head:ident, $($tail:ident),+ $(,)?) => {
245        crate::macros::felt_newtypes!(@generate_felt251_macro $head);
246        crate::macros::felt_newtypes!(@generate_felt251_macro $($tail),+);
247    };
248
249    (@generate_felt251_macro $target:ident) => {
250        paste::paste! {
251            #[macro_export]
252            macro_rules! [<$target:snake>] {
253                ($hex:expr) => {
254                    $crate::$target::new_or_panic($crate::felt!($hex))
255                };
256            }
257
258            #[macro_export]
259            macro_rules! [<$target:snake _bytes>] {
260                ($bytes:expr) => {
261                    $crate::$target::new_or_panic($crate::felt_bytes!($bytes))
262                };
263            }
264        }
265    };
266}
267pub(super) use felt_newtypes;
268
269pub(super) mod fmt {
270
271    /// Adds a thin display implementation which uses the inner fields Display.
272    macro_rules! thin_display {
273        ($target:ty) => {
274            impl std::fmt::Display for $target {
275                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276                    std::fmt::Display::fmt(&self.0, f)
277                }
278            }
279        };
280    }
281
282    /// Adds a thin Debug implementation, which skips `X(StarkHash(debug))` as
283    /// `X(debug)`.
284    ///
285    /// The implementation uses Display of the wrapped value to produce smallest
286    /// possible string, but still wraps it in a default Debug derive style
287    /// `TypeName(hash)`.
288    macro_rules! thin_debug {
289        ($target:ty) => {
290            impl std::fmt::Debug for $target {
291                fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292                    write!(fmt, "{}({})", stringify!($target), self.0)
293                }
294            }
295        };
296    }
297
298    pub(crate) use thin_debug;
299    pub(crate) use thin_display;
300}
301
302/// Creates a [Felt](pathfinder_crypto::Felt) from a hex string literal verified
303/// at compile time.
304#[macro_export]
305macro_rules! felt {
306    ($hex:expr) => {{
307        // This forces const evaluation of the macro call. Without this the invocation
308        // will only be evaluated at runtime.
309        use ::pathfinder_crypto;
310        const CONST_FELT: pathfinder_crypto::Felt =
311            match pathfinder_crypto::Felt::from_hex_str($hex) {
312                Ok(f) => f,
313                Err(pathfinder_crypto::HexParseError::InvalidNibble(_)) => {
314                    panic!("Invalid hex digit")
315                }
316                Err(pathfinder_crypto::HexParseError::InvalidLength { .. }) => {
317                    panic!("Too many hex digits")
318                }
319                Err(pathfinder_crypto::HexParseError::MissingPrefix) => {
320                    panic!("Missing '0x' prefix")
321                }
322                Err(pathfinder_crypto::HexParseError::Overflow) => panic!("Felt overflow"),
323            };
324        CONST_FELT
325    }};
326}
327
328/// Creates a [`pathfinder_crypto::Felt`] from a byte slice, resulting in
329/// compile-time error when invalid.
330#[macro_export]
331macro_rules! felt_bytes {
332    ($bytes:expr) => {{
333        match pathfinder_crypto::Felt::from_be_slice($bytes) {
334            Ok(sh) => sh,
335            Err(pathfinder_crypto::OverflowError) => panic!("Invalid constant: OverflowError"),
336        }
337    }};
338}