miden-crypto/src/hash/rpo/digest.rs
src/hash/rpo/digest.rsRaw
1use super::{Digest, Felt, StarkField, DIGEST_SIZE, ZERO};
2use crate::utils::{
3 string::String, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
4};
5use core::{cmp::Ordering, ops::Deref};
6
7// DIGEST TRAIT IMPLEMENTATIONS
8// ================================================================================================
9
10#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
11pub struct RpoDigest([Felt; DIGEST_SIZE]);
12
13impl RpoDigest {
14 pub fn new(value: [Felt; DIGEST_SIZE]) -> Self {
15 Self(value)
16 }
17
18 pub fn as_elements(&self) -> &[Felt] {
19 self.as_ref()
20 }
21
22 pub fn as_bytes(&self) -> [u8; 32] {
23 <Self as Digest>::as_bytes(self)
24 }
25
26 pub fn digests_as_elements<'a, I>(digests: I) -> impl Iterator<Item = &'a Felt>
27 where
28 I: Iterator<Item = &'a Self>,
29 {
30 digests.flat_map(|d| d.0.iter())
31 }
32}
33
34impl Digest for RpoDigest {
35 fn as_bytes(&self) -> [u8; 32] {
36 let mut result = [0; 32];
37
38 result[..8].copy_from_slice(&self.0[0].as_int().to_le_bytes());
39 result[8..16].copy_from_slice(&self.0[1].as_int().to_le_bytes());
40 result[16..24].copy_from_slice(&self.0[2].as_int().to_le_bytes());
41 result[24..].copy_from_slice(&self.0[3].as_int().to_le_bytes());
42
43 result
44 }
45}
46
47impl Serializable for RpoDigest {
48 fn write_into<W: ByteWriter>(&self, target: &mut W) {
49 target.write_u8_slice(&self.as_bytes());
50 }
51}
52
53impl Deserializable for RpoDigest {
54 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
55 let mut inner: [Felt; DIGEST_SIZE] = [ZERO; DIGEST_SIZE];
56 for inner in inner.iter_mut() {
57 let e = source.read_u64()?;
58 if e >= Felt::MODULUS {
59 return Err(DeserializationError::InvalidValue(String::from(
60 "Value not in the appropriate range",
61 )));
62 }
63 *inner = Felt::new(e);
64 }
65
66 Ok(Self(inner))
67 }
68}
69
70impl From<[Felt; DIGEST_SIZE]> for RpoDigest {
71 fn from(value: [Felt; DIGEST_SIZE]) -> Self {
72 Self(value)
73 }
74}
75
76impl From<RpoDigest> for [Felt; DIGEST_SIZE] {
77 fn from(value: RpoDigest) -> Self {
78 value.0
79 }
80}
81
82impl From<RpoDigest> for [u8; 32] {
83 fn from(value: RpoDigest) -> Self {
84 value.as_bytes()
85 }
86}
87
88impl Deref for RpoDigest {
89 type Target = [Felt; DIGEST_SIZE];
90
91 fn deref(&self) -> &Self::Target {
92 &self.0
93 }
94}
95
96impl Ord for RpoDigest {
97 fn cmp(&self, other: &Self) -> Ordering {
98 // compare the inner u64 of both elements.
99 //
100 // it will iterate the elements and will return the first computation different than
101 // `Equal`. Otherwise, the ordering is equal.
102 //
103 // the endianness is irrelevant here because since, this being a cryptographically secure
104 // hash computation, the digest shouldn't have any ordered property of its input.
105 //
106 // finally, we use `Felt::inner` instead of `Felt::as_int` so we avoid performing a
107 // montgomery reduction for every limb. that is safe because every inner element of the
108 // digest is guaranteed to be in its canonical form (that is, `x in [0,p)`).
109 self.0
110 .iter()
111 .map(Felt::inner)
112 .zip(other.0.iter().map(Felt::inner))
113 .fold(Ordering::Equal, |ord, (a, b)| match ord {
114 Ordering::Equal => a.cmp(&b),
115 _ => ord,
116 })
117 }
118}
119
120impl PartialOrd for RpoDigest {
121 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
122 Some(self.cmp(other))
123 }
124}
125
126// TESTS
127// ================================================================================================
128
129#[cfg(test)]
130mod tests {
131
132 use super::{Deserializable, Felt, RpoDigest, Serializable};
133 use crate::utils::SliceReader;
134 use rand_utils::rand_value;
135
136 #[test]
137 fn digest_serialization() {
138 let e1 = Felt::new(rand_value());
139 let e2 = Felt::new(rand_value());
140 let e3 = Felt::new(rand_value());
141 let e4 = Felt::new(rand_value());
142
143 let d1 = RpoDigest([e1, e2, e3, e4]);
144
145 let mut bytes = vec![];
146 d1.write_into(&mut bytes);
147 assert_eq!(32, bytes.len());
148
149 let mut reader = SliceReader::new(&bytes);
150 let d2 = RpoDigest::read_from(&mut reader).unwrap();
151
152 assert_eq!(d1, d2);
153 }
154}