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