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, 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 => Err(ConversionError::message(format!(
69 "not enough data, expected {DIGEST_DATA_SIZE}, got {size}"
70 ))),
71 size if size > DIGEST_DATA_SIZE => Err(ConversionError::message(format!(
72 "too much data, 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_canonical_u64(),
110 d1: value[1].as_canonical_u64(),
111 d2: value[2].as_canonical_u64(),
112 d3: value[3].as_canonical_u64(),
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_canonical_u64(),
127 d1: value[1].as_canonical_u64(),
128 d2: value[2].as_canonical_u64(),
129 d3: value[3].as_canonical_u64(),
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].iter().any(|v| *v >= Felt::ORDER) {
178 return Err(ConversionError::message("value is not in the range 0..MODULUS"));
179 }
180
181 Ok([
182 Felt::new(value.d0),
183 Felt::new(value.d1),
184 Felt::new(value.d2),
185 Felt::new(value.d3),
186 ])
187 }
188}
189
190impl TryFrom<proto::primitives::Digest> for Word {
191 type Error = ConversionError;
192
193 fn try_from(value: proto::primitives::Digest) -> Result<Self, Self::Error> {
194 Ok(Self::new(value.try_into()?))
195 }
196}
197
198impl TryFrom<proto::primitives::Digest> for StorageMapKey {
199 type Error = ConversionError;
200
201 fn try_from(value: proto::primitives::Digest) -> Result<Self, Self::Error> {
202 Ok(StorageMapKey::new(value.try_into()?))
203 }
204}
205
206impl TryFrom<&proto::primitives::Digest> for [Felt; 4] {
207 type Error = ConversionError;
208
209 fn try_from(value: &proto::primitives::Digest) -> Result<Self, Self::Error> {
210 (*value).try_into()
211 }
212}
213
214impl TryFrom<&proto::primitives::Digest> for Word {
215 type Error = ConversionError;
216
217 fn try_from(value: &proto::primitives::Digest) -> Result<Self, Self::Error> {
218 (*value).try_into()
219 }
220}
221
222#[cfg(test)]
226mod test {
227 use hex::{FromHex, ToHex};
228 use proptest::prelude::*;
229
230 use crate::generated as proto;
231
232 #[test]
233 fn hex_digest() {
234 let digest = proto::primitives::Digest {
235 d0: 0x306A_B7A6_F795_CAD7,
236 d1: 0x4927_3716_D099_AA04,
237 d2: 0xF741_2C3D_E726_4450,
238 d3: 0x976B_8764_9DB3_B82F,
239 };
240 let encoded: String = ToHex::encode_hex(&digest);
241 let round_trip: Result<proto::primitives::Digest, _> =
242 FromHex::from_hex::<&[u8]>(encoded.as_ref());
243 assert_eq!(digest, round_trip.unwrap());
244
245 let digest = proto::primitives::Digest { d0: 0, d1: 0, d2: 0, d3: 0 };
246 let encoded: String = ToHex::encode_hex(&digest);
247 let round_trip: Result<proto::primitives::Digest, _> =
248 FromHex::from_hex::<&[u8]>(encoded.as_ref());
249 assert_eq!(digest, round_trip.unwrap());
250 }
251
252 proptest! {
253 #[test]
254 fn encode_decode(
255 d0: u64,
256 d1: u64,
257 d2: u64,
258 d3: u64,
259 ) {
260 let digest = proto::primitives::Digest { d0, d1, d2, d3 };
261 let encoded: String = ToHex::encode_hex(&digest);
262 let round_trip: Result<proto::primitives::Digest, _> = FromHex::from_hex::<&[u8]>(encoded.as_ref());
263 assert_eq!(digest, round_trip.unwrap());
264 }
265 }
266}