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