miden_node_proto/domain/
digest.rs1use std::fmt::{Debug, Display, Formatter};
2
3use hex::{FromHex, ToHex};
4use miden_objects::note::NoteId;
5use miden_objects::{Felt, StarkField, Word};
6
7use crate::errors::ConversionError;
8use crate::generated as proto;
9
10pub const DIGEST_DATA_SIZE: usize = 32;
14
15impl Display for proto::primitives::Digest {
19 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
20 f.write_str(&self.encode_hex::<String>())
21 }
22}
23
24impl Debug for proto::primitives::Digest {
25 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
26 Display::fmt(self, f)
27 }
28}
29
30impl ToHex for &proto::primitives::Digest {
31 fn encode_hex<T: FromIterator<char>>(&self) -> T {
32 (*self).encode_hex()
33 }
34
35 fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
36 (*self).encode_hex_upper()
37 }
38}
39
40impl ToHex for proto::primitives::Digest {
41 fn encode_hex<T: FromIterator<char>>(&self) -> T {
42 let mut data: Vec<char> = Vec::with_capacity(DIGEST_DATA_SIZE);
43 data.extend(format!("{:016x}", self.d0).chars());
44 data.extend(format!("{:016x}", self.d1).chars());
45 data.extend(format!("{:016x}", self.d2).chars());
46 data.extend(format!("{:016x}", self.d3).chars());
47 data.into_iter().collect()
48 }
49
50 fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
51 let mut data: Vec<char> = Vec::with_capacity(DIGEST_DATA_SIZE);
52 data.extend(format!("{:016X}", self.d0).chars());
53 data.extend(format!("{:016X}", self.d1).chars());
54 data.extend(format!("{:016X}", self.d2).chars());
55 data.extend(format!("{:016X}", self.d3).chars());
56 data.into_iter().collect()
57 }
58}
59
60impl FromHex for proto::primitives::Digest {
61 type Error = ConversionError;
62
63 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
64 let data = hex::decode(hex)?;
65
66 match data.len() {
67 size if size < DIGEST_DATA_SIZE => {
68 Err(ConversionError::InsufficientData { expected: DIGEST_DATA_SIZE, got: size })
69 },
70 size if size > DIGEST_DATA_SIZE => {
71 Err(ConversionError::TooMuchData { expected: DIGEST_DATA_SIZE, got: size })
72 },
73 _ => {
74 let d0 = u64::from_be_bytes(data[..8].try_into().unwrap());
75 let d1 = u64::from_be_bytes(data[8..16].try_into().unwrap());
76 let d2 = u64::from_be_bytes(data[16..24].try_into().unwrap());
77 let d3 = u64::from_be_bytes(data[24..32].try_into().unwrap());
78
79 Ok(proto::primitives::Digest { d0, d1, d2, d3 })
80 },
81 }
82 }
83}
84
85impl From<[u64; 4]> for proto::primitives::Digest {
89 fn from(value: [u64; 4]) -> Self {
90 Self {
91 d0: value[0],
92 d1: value[1],
93 d2: value[2],
94 d3: value[3],
95 }
96 }
97}
98
99impl From<&[u64; 4]> for proto::primitives::Digest {
100 fn from(value: &[u64; 4]) -> Self {
101 (*value).into()
102 }
103}
104
105impl From<[Felt; 4]> for proto::primitives::Digest {
106 fn from(value: [Felt; 4]) -> Self {
107 Self {
108 d0: value[0].as_int(),
109 d1: value[1].as_int(),
110 d2: value[2].as_int(),
111 d3: value[3].as_int(),
112 }
113 }
114}
115
116impl From<&[Felt; 4]> for proto::primitives::Digest {
117 fn from(value: &[Felt; 4]) -> Self {
118 (*value).into()
119 }
120}
121
122impl From<Word> for proto::primitives::Digest {
123 fn from(value: Word) -> Self {
124 Self {
125 d0: value[0].as_int(),
126 d1: value[1].as_int(),
127 d2: value[2].as_int(),
128 d3: value[3].as_int(),
129 }
130 }
131}
132
133impl From<&Word> for proto::primitives::Digest {
134 fn from(value: &Word) -> Self {
135 (*value).into()
136 }
137}
138
139impl From<&NoteId> for proto::primitives::Digest {
140 fn from(value: &NoteId) -> Self {
141 value.as_word().into()
142 }
143}
144
145impl From<NoteId> for proto::primitives::Digest {
146 fn from(value: NoteId) -> Self {
147 value.as_word().into()
148 }
149}
150
151impl From<proto::primitives::Digest> for [u64; 4] {
155 fn from(value: proto::primitives::Digest) -> Self {
156 [value.d0, value.d1, value.d2, value.d3]
157 }
158}
159
160impl TryFrom<proto::primitives::Digest> for [Felt; 4] {
161 type Error = ConversionError;
162
163 fn try_from(value: proto::primitives::Digest) -> Result<Self, Self::Error> {
164 if [value.d0, value.d1, value.d2, value.d3]
165 .iter()
166 .any(|v| *v >= <Felt as StarkField>::MODULUS)
167 {
168 return Err(ConversionError::NotAValidFelt);
169 }
170
171 Ok([
172 Felt::new(value.d0),
173 Felt::new(value.d1),
174 Felt::new(value.d2),
175 Felt::new(value.d3),
176 ])
177 }
178}
179
180impl TryFrom<proto::primitives::Digest> for Word {
181 type Error = ConversionError;
182
183 fn try_from(value: proto::primitives::Digest) -> Result<Self, Self::Error> {
184 Ok(Self::new(value.try_into()?))
185 }
186}
187
188impl TryFrom<&proto::primitives::Digest> for [Felt; 4] {
189 type Error = ConversionError;
190
191 fn try_from(value: &proto::primitives::Digest) -> Result<Self, Self::Error> {
192 (*value).try_into()
193 }
194}
195
196impl TryFrom<&proto::primitives::Digest> for Word {
197 type Error = ConversionError;
198
199 fn try_from(value: &proto::primitives::Digest) -> Result<Self, Self::Error> {
200 (*value).try_into()
201 }
202}
203
204#[cfg(test)]
208mod test {
209 use hex::{FromHex, ToHex};
210 use proptest::prelude::*;
211
212 use crate::generated as proto;
213
214 #[test]
215 fn hex_digest() {
216 let digest = proto::primitives::Digest {
217 d0: 0x306A_B7A6_F795_CAD7,
218 d1: 0x4927_3716_D099_AA04,
219 d2: 0xF741_2C3D_E726_4450,
220 d3: 0x976B_8764_9DB3_B82F,
221 };
222 let encoded: String = ToHex::encode_hex(&digest);
223 let round_trip: Result<proto::primitives::Digest, _> =
224 FromHex::from_hex::<&[u8]>(encoded.as_ref());
225 assert_eq!(digest, round_trip.unwrap());
226
227 let digest = proto::primitives::Digest { d0: 0, d1: 0, d2: 0, d3: 0 };
228 let encoded: String = ToHex::encode_hex(&digest);
229 let round_trip: Result<proto::primitives::Digest, _> =
230 FromHex::from_hex::<&[u8]>(encoded.as_ref());
231 assert_eq!(digest, round_trip.unwrap());
232 }
233
234 proptest! {
235 #[test]
236 fn encode_decode(
237 d0: u64,
238 d1: u64,
239 d2: u64,
240 d3: u64,
241 ) {
242 let digest = proto::primitives::Digest { d0, d1, d2, d3 };
243 let encoded: String = ToHex::encode_hex(&digest);
244 let round_trip: Result<proto::primitives::Digest, _> = FromHex::from_hex::<&[u8]>(encoded.as_ref());
245 assert_eq!(digest, round_trip.unwrap());
246 }
247 }
248}