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 const 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_bytes(&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 [Felt; DIGEST_SIZE] {
83 fn from(value: RpoDigest) -> Self {
84 value.0
85 }
86}
87
88impl From<&RpoDigest> for [u8; 32] {
89 fn from(value: &RpoDigest) -> Self {
90 value.as_bytes()
91 }
92}
93
94impl From<RpoDigest> for [u8; 32] {
95 fn from(value: RpoDigest) -> Self {
96 value.as_bytes()
97 }
98}
99
100impl Deref for RpoDigest {
101 type Target = [Felt; DIGEST_SIZE];
102
103 fn deref(&self) -> &Self::Target {
104 &self.0
105 }
106}
107
108impl Ord for RpoDigest {
109 fn cmp(&self, other: &Self) -> Ordering {
110 // compare the inner u64 of both elements.
111 //
112 // it will iterate the elements and will return the first computation different than
113 // `Equal`. Otherwise, the ordering is equal.
114 //
115 // the endianness is irrelevant here because since, this being a cryptographically secure
116 // hash computation, the digest shouldn't have any ordered property of its input.
117 //
118 // finally, we use `Felt::inner` instead of `Felt::as_int` so we avoid performing a
119 // montgomery reduction for every limb. that is safe because every inner element of the
120 // digest is guaranteed to be in its canonical form (that is, `x in [0,p)`).
121 self.0
122 .iter()
123 .map(Felt::inner)
124 .zip(other.0.iter().map(Felt::inner))
125 .fold(Ordering::Equal, |ord, (a, b)| match ord {
126 Ordering::Equal => a.cmp(&b),
127 _ => ord,
128 })
129 }
130}
131
132impl PartialOrd for RpoDigest {
133 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
134 Some(self.cmp(other))
135 }
136}
137
138// TESTS
139// ================================================================================================
140
141#[cfg(test)]
142mod tests {
143
144 use super::{Deserializable, Felt, RpoDigest, Serializable};
145 use crate::utils::SliceReader;
146 use rand_utils::rand_value;
147
148 #[test]
149 fn digest_serialization() {
150 let e1 = Felt::new(rand_value());
151 let e2 = Felt::new(rand_value());
152 let e3 = Felt::new(rand_value());
153 let e4 = Felt::new(rand_value());
154
155 let d1 = RpoDigest([e1, e2, e3, e4]);
156
157 let mut bytes = vec![];
158 d1.write_into(&mut bytes);
159 assert_eq!(32, bytes.len());
160
161 let mut reader = SliceReader::new(&bytes);
162 let d2 = RpoDigest::read_from(&mut reader).unwrap();
163
164 assert_eq!(d1, d2);
165 }
166}