Skip to main content

miden_node_proto/domain/
digest.rs

1use 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
11// CONSTANTS
12// ================================================================================================
13
14pub const DIGEST_DATA_SIZE: usize = 32;
15
16// FORMATTING
17// ================================================================================================
18
19impl 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
86// INTO
87// ================================================================================================
88
89impl 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
164// FROM DIGEST
165// ================================================================================================
166
167impl 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// TESTS
226// ================================================================================================
227
228#[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}