miden_node_proto/domain/
digest.rs

1use 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
8// CONSTANTS
9// ================================================================================================
10
11pub const DIGEST_DATA_SIZE: usize = 32;
12
13// FORMATTING
14// ================================================================================================
15
16impl 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
83// INTO
84// ================================================================================================
85
86impl 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
149// FROM DIGEST
150// ================================================================================================
151
152impl 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// TESTS
203// ================================================================================================
204
205#[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}