1use alloc::string::String;
2use core::{cmp::Ordering, fmt::Display, ops::Deref, slice};
3
4use thiserror::Error;
5
6use super::{Digest, Felt, StarkField, DIGEST_BYTES, DIGEST_SIZE, ZERO};
7use crate::{
8 rand::Randomizable,
9 utils::{
10 bytes_to_hex_string, hex_to_bytes, ByteReader, ByteWriter, Deserializable,
11 DeserializationError, HexParseError, Serializable,
12 },
13};
14
15#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
19#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
20#[cfg_attr(feature = "serde", serde(into = "String", try_from = "&str"))]
21pub struct RpxDigest([Felt; DIGEST_SIZE]);
22
23impl RpxDigest {
24 pub const SERIALIZED_SIZE: usize = DIGEST_BYTES;
26
27 pub const fn new(value: [Felt; DIGEST_SIZE]) -> Self {
28 Self(value)
29 }
30
31 pub fn as_elements(&self) -> &[Felt] {
32 self.as_ref()
33 }
34
35 pub fn as_bytes(&self) -> [u8; DIGEST_BYTES] {
36 <Self as Digest>::as_bytes(self)
37 }
38
39 pub fn digests_as_elements_iter<'a, I>(digests: I) -> impl Iterator<Item = &'a Felt>
40 where
41 I: Iterator<Item = &'a Self>,
42 {
43 digests.flat_map(|d| d.0.iter())
44 }
45
46 pub fn digests_as_elements(digests: &[Self]) -> &[Felt] {
47 let p = digests.as_ptr();
48 let len = digests.len() * DIGEST_SIZE;
49 unsafe { slice::from_raw_parts(p as *const Felt, len) }
50 }
51
52 pub fn to_hex(&self) -> String {
54 bytes_to_hex_string(self.as_bytes())
55 }
56}
57
58impl Digest for RpxDigest {
59 fn as_bytes(&self) -> [u8; DIGEST_BYTES] {
60 let mut result = [0; DIGEST_BYTES];
61
62 result[..8].copy_from_slice(&self.0[0].as_int().to_le_bytes());
63 result[8..16].copy_from_slice(&self.0[1].as_int().to_le_bytes());
64 result[16..24].copy_from_slice(&self.0[2].as_int().to_le_bytes());
65 result[24..].copy_from_slice(&self.0[3].as_int().to_le_bytes());
66
67 result
68 }
69}
70
71impl Deref for RpxDigest {
72 type Target = [Felt; DIGEST_SIZE];
73
74 fn deref(&self) -> &Self::Target {
75 &self.0
76 }
77}
78
79impl Ord for RpxDigest {
80 fn cmp(&self, other: &Self) -> Ordering {
81 self.0.iter().map(Felt::inner).zip(other.0.iter().map(Felt::inner)).fold(
93 Ordering::Equal,
94 |ord, (a, b)| match ord {
95 Ordering::Equal => a.cmp(&b),
96 _ => ord,
97 },
98 )
99 }
100}
101
102impl PartialOrd for RpxDigest {
103 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
104 Some(self.cmp(other))
105 }
106}
107
108impl Display for RpxDigest {
109 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110 let encoded: String = self.into();
111 write!(f, "{}", encoded)?;
112 Ok(())
113 }
114}
115
116impl Randomizable for RpxDigest {
117 const VALUE_SIZE: usize = DIGEST_BYTES;
118
119 fn from_random_bytes(bytes: &[u8]) -> Option<Self> {
120 let bytes_array: Option<[u8; 32]> = bytes.try_into().ok();
121 if let Some(bytes_array) = bytes_array {
122 Self::try_from(bytes_array).ok()
123 } else {
124 None
125 }
126 }
127}
128
129#[derive(Debug, Error)]
133pub enum RpxDigestError {
134 #[error("failed to convert digest field element to {0}")]
135 TypeConversion(&'static str),
136 #[error("failed to convert to field element: {0}")]
137 InvalidFieldElement(String),
138}
139
140impl TryFrom<&RpxDigest> for [bool; DIGEST_SIZE] {
141 type Error = RpxDigestError;
142
143 fn try_from(value: &RpxDigest) -> Result<Self, Self::Error> {
144 (*value).try_into()
145 }
146}
147
148impl TryFrom<RpxDigest> for [bool; DIGEST_SIZE] {
149 type Error = RpxDigestError;
150
151 fn try_from(value: RpxDigest) -> Result<Self, Self::Error> {
152 fn to_bool(v: u64) -> Option<bool> {
153 if v <= 1 {
154 Some(v == 1)
155 } else {
156 None
157 }
158 }
159
160 Ok([
161 to_bool(value.0[0].as_int()).ok_or(RpxDigestError::TypeConversion("bool"))?,
162 to_bool(value.0[1].as_int()).ok_or(RpxDigestError::TypeConversion("bool"))?,
163 to_bool(value.0[2].as_int()).ok_or(RpxDigestError::TypeConversion("bool"))?,
164 to_bool(value.0[3].as_int()).ok_or(RpxDigestError::TypeConversion("bool"))?,
165 ])
166 }
167}
168
169impl TryFrom<&RpxDigest> for [u8; DIGEST_SIZE] {
170 type Error = RpxDigestError;
171
172 fn try_from(value: &RpxDigest) -> Result<Self, Self::Error> {
173 (*value).try_into()
174 }
175}
176
177impl TryFrom<RpxDigest> for [u8; DIGEST_SIZE] {
178 type Error = RpxDigestError;
179
180 fn try_from(value: RpxDigest) -> Result<Self, Self::Error> {
181 Ok([
182 value.0[0]
183 .as_int()
184 .try_into()
185 .map_err(|_| RpxDigestError::TypeConversion("u8"))?,
186 value.0[1]
187 .as_int()
188 .try_into()
189 .map_err(|_| RpxDigestError::TypeConversion("u8"))?,
190 value.0[2]
191 .as_int()
192 .try_into()
193 .map_err(|_| RpxDigestError::TypeConversion("u8"))?,
194 value.0[3]
195 .as_int()
196 .try_into()
197 .map_err(|_| RpxDigestError::TypeConversion("u8"))?,
198 ])
199 }
200}
201
202impl TryFrom<&RpxDigest> for [u16; DIGEST_SIZE] {
203 type Error = RpxDigestError;
204
205 fn try_from(value: &RpxDigest) -> Result<Self, Self::Error> {
206 (*value).try_into()
207 }
208}
209
210impl TryFrom<RpxDigest> for [u16; DIGEST_SIZE] {
211 type Error = RpxDigestError;
212
213 fn try_from(value: RpxDigest) -> Result<Self, Self::Error> {
214 Ok([
215 value.0[0]
216 .as_int()
217 .try_into()
218 .map_err(|_| RpxDigestError::TypeConversion("u16"))?,
219 value.0[1]
220 .as_int()
221 .try_into()
222 .map_err(|_| RpxDigestError::TypeConversion("u16"))?,
223 value.0[2]
224 .as_int()
225 .try_into()
226 .map_err(|_| RpxDigestError::TypeConversion("u16"))?,
227 value.0[3]
228 .as_int()
229 .try_into()
230 .map_err(|_| RpxDigestError::TypeConversion("u16"))?,
231 ])
232 }
233}
234
235impl TryFrom<&RpxDigest> for [u32; DIGEST_SIZE] {
236 type Error = RpxDigestError;
237
238 fn try_from(value: &RpxDigest) -> Result<Self, Self::Error> {
239 (*value).try_into()
240 }
241}
242
243impl TryFrom<RpxDigest> for [u32; DIGEST_SIZE] {
244 type Error = RpxDigestError;
245
246 fn try_from(value: RpxDigest) -> Result<Self, Self::Error> {
247 Ok([
248 value.0[0]
249 .as_int()
250 .try_into()
251 .map_err(|_| RpxDigestError::TypeConversion("u32"))?,
252 value.0[1]
253 .as_int()
254 .try_into()
255 .map_err(|_| RpxDigestError::TypeConversion("u32"))?,
256 value.0[2]
257 .as_int()
258 .try_into()
259 .map_err(|_| RpxDigestError::TypeConversion("u32"))?,
260 value.0[3]
261 .as_int()
262 .try_into()
263 .map_err(|_| RpxDigestError::TypeConversion("u32"))?,
264 ])
265 }
266}
267
268impl From<&RpxDigest> for [u64; DIGEST_SIZE] {
269 fn from(value: &RpxDigest) -> Self {
270 (*value).into()
271 }
272}
273
274impl From<RpxDigest> for [u64; DIGEST_SIZE] {
275 fn from(value: RpxDigest) -> Self {
276 [
277 value.0[0].as_int(),
278 value.0[1].as_int(),
279 value.0[2].as_int(),
280 value.0[3].as_int(),
281 ]
282 }
283}
284
285impl From<&RpxDigest> for [Felt; DIGEST_SIZE] {
286 fn from(value: &RpxDigest) -> Self {
287 value.0
288 }
289}
290
291impl From<RpxDigest> for [Felt; DIGEST_SIZE] {
292 fn from(value: RpxDigest) -> Self {
293 value.0
294 }
295}
296
297impl From<&RpxDigest> for [u8; DIGEST_BYTES] {
298 fn from(value: &RpxDigest) -> Self {
299 value.as_bytes()
300 }
301}
302
303impl From<RpxDigest> for [u8; DIGEST_BYTES] {
304 fn from(value: RpxDigest) -> Self {
305 value.as_bytes()
306 }
307}
308
309impl From<&RpxDigest> for String {
310 fn from(value: &RpxDigest) -> Self {
312 (*value).into()
313 }
314}
315
316impl From<RpxDigest> for String {
317 fn from(value: RpxDigest) -> Self {
319 value.to_hex()
320 }
321}
322
323impl From<&[bool; DIGEST_SIZE]> for RpxDigest {
327 fn from(value: &[bool; DIGEST_SIZE]) -> Self {
328 (*value).into()
329 }
330}
331
332impl From<[bool; DIGEST_SIZE]> for RpxDigest {
333 fn from(value: [bool; DIGEST_SIZE]) -> Self {
334 [value[0] as u32, value[1] as u32, value[2] as u32, value[3] as u32].into()
335 }
336}
337
338impl From<&[u8; DIGEST_SIZE]> for RpxDigest {
339 fn from(value: &[u8; DIGEST_SIZE]) -> Self {
340 (*value).into()
341 }
342}
343
344impl From<[u8; DIGEST_SIZE]> for RpxDigest {
345 fn from(value: [u8; DIGEST_SIZE]) -> Self {
346 Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()])
347 }
348}
349
350impl From<&[u16; DIGEST_SIZE]> for RpxDigest {
351 fn from(value: &[u16; DIGEST_SIZE]) -> Self {
352 (*value).into()
353 }
354}
355
356impl From<[u16; DIGEST_SIZE]> for RpxDigest {
357 fn from(value: [u16; DIGEST_SIZE]) -> Self {
358 Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()])
359 }
360}
361
362impl From<&[u32; DIGEST_SIZE]> for RpxDigest {
363 fn from(value: &[u32; DIGEST_SIZE]) -> Self {
364 (*value).into()
365 }
366}
367
368impl From<[u32; DIGEST_SIZE]> for RpxDigest {
369 fn from(value: [u32; DIGEST_SIZE]) -> Self {
370 Self([value[0].into(), value[1].into(), value[2].into(), value[3].into()])
371 }
372}
373
374impl TryFrom<&[u64; DIGEST_SIZE]> for RpxDigest {
375 type Error = RpxDigestError;
376
377 fn try_from(value: &[u64; DIGEST_SIZE]) -> Result<Self, RpxDigestError> {
378 (*value).try_into()
379 }
380}
381
382impl TryFrom<[u64; DIGEST_SIZE]> for RpxDigest {
383 type Error = RpxDigestError;
384
385 fn try_from(value: [u64; DIGEST_SIZE]) -> Result<Self, RpxDigestError> {
386 Ok(Self([
387 value[0].try_into().map_err(RpxDigestError::InvalidFieldElement)?,
388 value[1].try_into().map_err(RpxDigestError::InvalidFieldElement)?,
389 value[2].try_into().map_err(RpxDigestError::InvalidFieldElement)?,
390 value[3].try_into().map_err(RpxDigestError::InvalidFieldElement)?,
391 ]))
392 }
393}
394
395impl From<&[Felt; DIGEST_SIZE]> for RpxDigest {
396 fn from(value: &[Felt; DIGEST_SIZE]) -> Self {
397 Self(*value)
398 }
399}
400
401impl From<[Felt; DIGEST_SIZE]> for RpxDigest {
402 fn from(value: [Felt; DIGEST_SIZE]) -> Self {
403 Self(value)
404 }
405}
406
407impl TryFrom<&[u8; DIGEST_BYTES]> for RpxDigest {
408 type Error = HexParseError;
409
410 fn try_from(value: &[u8; DIGEST_BYTES]) -> Result<Self, Self::Error> {
411 (*value).try_into()
412 }
413}
414
415impl TryFrom<[u8; DIGEST_BYTES]> for RpxDigest {
416 type Error = HexParseError;
417
418 fn try_from(value: [u8; DIGEST_BYTES]) -> Result<Self, Self::Error> {
419 let a = u64::from_le_bytes(value[0..8].try_into().unwrap());
422 let b = u64::from_le_bytes(value[8..16].try_into().unwrap());
423 let c = u64::from_le_bytes(value[16..24].try_into().unwrap());
424 let d = u64::from_le_bytes(value[24..32].try_into().unwrap());
425
426 if [a, b, c, d].iter().any(|v| *v >= Felt::MODULUS) {
427 return Err(HexParseError::OutOfRange);
428 }
429
430 Ok(RpxDigest([Felt::new(a), Felt::new(b), Felt::new(c), Felt::new(d)]))
431 }
432}
433
434impl TryFrom<&[u8]> for RpxDigest {
435 type Error = HexParseError;
436
437 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
438 (*value).try_into()
439 }
440}
441
442impl TryFrom<&str> for RpxDigest {
443 type Error = HexParseError;
444
445 fn try_from(value: &str) -> Result<Self, Self::Error> {
447 hex_to_bytes::<DIGEST_BYTES>(value).and_then(RpxDigest::try_from)
448 }
449}
450
451impl TryFrom<&String> for RpxDigest {
452 type Error = HexParseError;
453
454 fn try_from(value: &String) -> Result<Self, Self::Error> {
456 value.as_str().try_into()
457 }
458}
459
460impl TryFrom<String> for RpxDigest {
461 type Error = HexParseError;
462
463 fn try_from(value: String) -> Result<Self, Self::Error> {
465 value.as_str().try_into()
466 }
467}
468
469impl Serializable for RpxDigest {
473 fn write_into<W: ByteWriter>(&self, target: &mut W) {
474 target.write_bytes(&self.as_bytes());
475 }
476
477 fn get_size_hint(&self) -> usize {
478 Self::SERIALIZED_SIZE
479 }
480}
481
482impl Deserializable for RpxDigest {
483 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
484 let mut inner: [Felt; DIGEST_SIZE] = [ZERO; DIGEST_SIZE];
485 for inner in inner.iter_mut() {
486 let e = source.read_u64()?;
487 if e >= Felt::MODULUS {
488 return Err(DeserializationError::InvalidValue(String::from(
489 "Value not in the appropriate range",
490 )));
491 }
492 *inner = Felt::new(e);
493 }
494
495 Ok(Self(inner))
496 }
497}
498
499impl IntoIterator for RpxDigest {
502 type Item = Felt;
503 type IntoIter = <[Felt; 4] as IntoIterator>::IntoIter;
504
505 fn into_iter(self) -> Self::IntoIter {
506 self.0.into_iter()
507 }
508}
509
510#[cfg(test)]
514mod tests {
515 use alloc::string::String;
516
517 use rand_utils::rand_value;
518
519 use super::{Deserializable, Felt, RpxDigest, Serializable, DIGEST_BYTES, DIGEST_SIZE};
520 use crate::utils::SliceReader;
521
522 #[test]
523 fn digest_serialization() {
524 let e1 = Felt::new(rand_value());
525 let e2 = Felt::new(rand_value());
526 let e3 = Felt::new(rand_value());
527 let e4 = Felt::new(rand_value());
528
529 let d1 = RpxDigest([e1, e2, e3, e4]);
530
531 let mut bytes = vec![];
532 d1.write_into(&mut bytes);
533 assert_eq!(DIGEST_BYTES, bytes.len());
534 assert_eq!(bytes.len(), d1.get_size_hint());
535
536 let mut reader = SliceReader::new(&bytes);
537 let d2 = RpxDigest::read_from(&mut reader).unwrap();
538
539 assert_eq!(d1, d2);
540 }
541
542 #[test]
543 fn digest_encoding() {
544 let digest = RpxDigest([
545 Felt::new(rand_value()),
546 Felt::new(rand_value()),
547 Felt::new(rand_value()),
548 Felt::new(rand_value()),
549 ]);
550
551 let string: String = digest.into();
552 let round_trip: RpxDigest = string.try_into().expect("decoding failed");
553
554 assert_eq!(digest, round_trip);
555 }
556
557 #[test]
558 fn test_conversions() {
559 let digest = RpxDigest([
560 Felt::new(rand_value()),
561 Felt::new(rand_value()),
562 Felt::new(rand_value()),
563 Felt::new(rand_value()),
564 ]);
565
566 let v: [bool; DIGEST_SIZE] = [true, false, true, true];
569 let v2: RpxDigest = v.into();
570 assert_eq!(v, <[bool; DIGEST_SIZE]>::try_from(v2).unwrap());
571
572 let v: [u8; DIGEST_SIZE] = [0_u8, 1_u8, 2_u8, 3_u8];
573 let v2: RpxDigest = v.into();
574 assert_eq!(v, <[u8; DIGEST_SIZE]>::try_from(v2).unwrap());
575
576 let v: [u16; DIGEST_SIZE] = [0_u16, 1_u16, 2_u16, 3_u16];
577 let v2: RpxDigest = v.into();
578 assert_eq!(v, <[u16; DIGEST_SIZE]>::try_from(v2).unwrap());
579
580 let v: [u32; DIGEST_SIZE] = [0_u32, 1_u32, 2_u32, 3_u32];
581 let v2: RpxDigest = v.into();
582 assert_eq!(v, <[u32; DIGEST_SIZE]>::try_from(v2).unwrap());
583
584 let v: [u64; DIGEST_SIZE] = digest.into();
585 let v2: RpxDigest = v.try_into().unwrap();
586 assert_eq!(digest, v2);
587
588 let v: [Felt; DIGEST_SIZE] = digest.into();
589 let v2: RpxDigest = v.into();
590 assert_eq!(digest, v2);
591
592 let v: [u8; DIGEST_BYTES] = digest.into();
593 let v2: RpxDigest = v.try_into().unwrap();
594 assert_eq!(digest, v2);
595
596 let v: String = digest.into();
597 let v2: RpxDigest = v.try_into().unwrap();
598 assert_eq!(digest, v2);
599
600 let v: [bool; DIGEST_SIZE] = [true, false, true, true];
603 let v2: RpxDigest = (&v).into();
604 assert_eq!(v, <[bool; DIGEST_SIZE]>::try_from(&v2).unwrap());
605
606 let v: [u8; DIGEST_SIZE] = [0_u8, 1_u8, 2_u8, 3_u8];
607 let v2: RpxDigest = (&v).into();
608 assert_eq!(v, <[u8; DIGEST_SIZE]>::try_from(&v2).unwrap());
609
610 let v: [u16; DIGEST_SIZE] = [0_u16, 1_u16, 2_u16, 3_u16];
611 let v2: RpxDigest = (&v).into();
612 assert_eq!(v, <[u16; DIGEST_SIZE]>::try_from(&v2).unwrap());
613
614 let v: [u32; DIGEST_SIZE] = [0_u32, 1_u32, 2_u32, 3_u32];
615 let v2: RpxDigest = (&v).into();
616 assert_eq!(v, <[u32; DIGEST_SIZE]>::try_from(&v2).unwrap());
617
618 let v: [u64; DIGEST_SIZE] = (&digest).into();
619 let v2: RpxDigest = (&v).try_into().unwrap();
620 assert_eq!(digest, v2);
621
622 let v: [Felt; DIGEST_SIZE] = (&digest).into();
623 let v2: RpxDigest = (&v).into();
624 assert_eq!(digest, v2);
625
626 let v: [u8; DIGEST_BYTES] = (&digest).into();
627 let v2: RpxDigest = (&v).try_into().unwrap();
628 assert_eq!(digest, v2);
629
630 let v: String = (&digest).into();
631 let v2: RpxDigest = (&v).try_into().unwrap();
632 assert_eq!(digest, v2);
633 }
634}