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