1use alloc::{format, vec::Vec};
19use core::fmt::{Debug, Display, Formatter};
20
21use borsh::{BorshDeserialize, BorshSerialize};
22use bytemuck::{Pod, PodCastError, Zeroable};
23use hex::{FromHex, FromHexError};
24use serde::{Deserialize, Serialize};
25
26pub use crate::digest;
27pub use hex_literal::hex;
28pub use risc0_zkvm_platform::WORD_SIZE;
29
30pub const DIGEST_WORDS: usize = 8;
32
33pub const DIGEST_SHORTS: usize = DIGEST_WORDS * 2;
35
36pub const DIGEST_BYTES: usize = DIGEST_WORDS * WORD_SIZE;
40
41#[derive(
47 Copy,
48 Clone,
49 Eq,
50 Ord,
51 PartialOrd,
52 PartialEq,
53 Hash,
54 Pod,
55 Zeroable,
56 Serialize,
57 Deserialize,
58 BorshSerialize,
59 BorshDeserialize,
60)]
61#[repr(transparent)]
62pub struct Digest([u32; DIGEST_WORDS]);
63
64impl Digest {
65 pub const ZERO: Self = Self::new([0u32; DIGEST_WORDS]);
67
68 pub const fn new(data: [u32; DIGEST_WORDS]) -> Self {
70 Self(data)
71 }
72
73 pub const fn from_bytes(bytes: [u8; DIGEST_BYTES]) -> Self {
76 let mut digest: Digest = Digest::ZERO;
77 let mut i: usize = 0;
78 while i < DIGEST_WORDS {
79 let mut j = 0;
80 let mut word = 0u32;
81 while j < WORD_SIZE {
82 word <<= 8;
83 word |= bytes[i * WORD_SIZE + j] as u32;
84 j += 1;
85 }
86 word = u32::from_be(word);
87 digest.0[i] = word;
88 i += 1;
89 }
90 digest
91 }
92
93 pub fn as_words(&self) -> &[u32] {
95 &self.0
96 }
97
98 pub fn as_bytes(&self) -> &[u8] {
100 bytemuck::cast_slice(&self.0)
101 }
102
103 pub fn as_mut_words(&mut self) -> &mut [u32] {
105 &mut self.0
106 }
107
108 pub fn as_mut_bytes(&mut self) -> &mut [u8] {
110 bytemuck::cast_slice_mut(&mut self.0)
111 }
112}
113
114impl Default for Digest {
115 fn default() -> Digest {
116 Digest([0; DIGEST_WORDS])
117 }
118}
119
120impl From<[u32; DIGEST_WORDS]> for Digest {
122 fn from(data: [u32; DIGEST_WORDS]) -> Self {
123 Self(data)
124 }
125}
126
127impl From<[u8; DIGEST_BYTES]> for Digest {
129 fn from(data: [u8; DIGEST_BYTES]) -> Self {
130 match bytemuck::try_cast(data) {
131 Ok(digest) => digest,
132 Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
133 bytemuck::pod_read_unaligned(&data)
135 }
136 Err(e) => unreachable!("failed to cast [u8; DIGEST_BYTES] to Digest: {}", e),
137 }
138 }
139}
140
141impl<'a> From<&'a [u32; DIGEST_WORDS]> for &'a Digest {
142 fn from(data: &'a [u32; DIGEST_WORDS]) -> Self {
143 bytemuck::cast_ref(data)
144 }
145}
146
147impl FromHex for Digest {
148 type Error = FromHexError;
149
150 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
151 Ok(<[u8; DIGEST_BYTES]>::from_hex(hex)?.into())
152 }
153}
154
155impl TryFrom<&[u8]> for Digest {
156 type Error = core::array::TryFromSliceError;
157
158 fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
159 Ok(<[u8; DIGEST_BYTES]>::try_from(data)?.into())
160 }
161}
162
163impl TryFrom<&[u32]> for Digest {
164 type Error = core::array::TryFromSliceError;
165
166 fn try_from(data: &[u32]) -> Result<Self, Self::Error> {
167 Ok(<[u32; DIGEST_WORDS]>::try_from(data)?.into())
168 }
169}
170
171impl<'a> TryFrom<&'a [u32]> for &'a Digest {
172 type Error = PodCastError;
173
174 fn try_from(data: &'a [u32]) -> Result<Self, Self::Error> {
175 match bytemuck::try_cast_slice(data) {
176 Ok([digest]) => Ok(digest),
177 Ok(_) => Err(PodCastError::SizeMismatch),
178 Err(e) => Err(e),
179 }
180 }
181}
182
183impl TryFrom<Vec<u8>> for Digest {
184 type Error = Vec<u8>;
185
186 fn try_from(data: Vec<u8>) -> Result<Self, Self::Error> {
187 Ok(<[u8; DIGEST_BYTES]>::try_from(data)?.into())
188 }
189}
190
191impl TryFrom<Vec<u32>> for Digest {
192 type Error = Vec<u32>;
193
194 fn try_from(data: Vec<u32>) -> Result<Self, Self::Error> {
195 Ok(<[u32; DIGEST_WORDS]>::try_from(data)?.into())
196 }
197}
198
199impl From<Digest> for [u8; DIGEST_BYTES] {
200 fn from(digest: Digest) -> Self {
201 bytemuck::cast(digest)
202 }
203}
204
205impl From<Digest> for [u32; DIGEST_WORDS] {
206 fn from(digest: Digest) -> Self {
207 digest.0
208 }
209}
210
211impl AsRef<[u8; DIGEST_BYTES]> for Digest {
212 fn as_ref(&self) -> &[u8; DIGEST_BYTES] {
213 bytemuck::cast_ref(&self.0)
214 }
215}
216
217impl AsMut<[u8; DIGEST_BYTES]> for Digest {
218 fn as_mut(&mut self) -> &mut [u8; DIGEST_BYTES] {
219 bytemuck::cast_mut(&mut self.0)
220 }
221}
222
223impl AsRef<[u32; DIGEST_WORDS]> for Digest {
224 fn as_ref(&self) -> &[u32; DIGEST_WORDS] {
225 &self.0
226 }
227}
228
229impl AsMut<[u32; DIGEST_WORDS]> for Digest {
230 fn as_mut(&mut self) -> &mut [u32; DIGEST_WORDS] {
231 &mut self.0
232 }
233}
234
235impl AsRef<[u8]> for Digest {
236 fn as_ref(&self) -> &[u8] {
237 self.as_bytes()
238 }
239}
240
241impl AsMut<[u8]> for Digest {
242 fn as_mut(&mut self) -> &mut [u8] {
243 self.as_mut_bytes()
244 }
245}
246
247impl AsRef<[u32]> for Digest {
248 fn as_ref(&self) -> &[u32] {
249 self.as_words()
250 }
251}
252
253impl AsMut<[u32]> for Digest {
254 fn as_mut(&mut self) -> &mut [u32] {
255 self.as_mut_words()
256 }
257}
258
259impl Display for Digest {
260 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
261 f.write_str(&hex::encode(self))
262 }
263}
264
265impl Debug for Digest {
266 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
267 f.write_str(&format!("Digest({})", &hex::encode(self)))
268 }
269}
270
271#[macro_export]
273macro_rules! digest {
274 ($s:literal) => {{
275 const BYTES: [u8; $crate::core::digest::DIGEST_BYTES] = $crate::core::digest::hex!($s);
276 $crate::core::digest::Digest::from_bytes(BYTES)
277 }};
278}
279
280#[cfg(test)]
281mod tests {
282 use hex::FromHex;
283
284 use super::Digest;
285
286 #[test]
287 fn test_from_hex() {
288 assert_eq!(
289 Digest::from_hex("00000077000000AA0000001200000034000000560000007a000000a900000009")
290 .unwrap(),
291 Digest::from([
292 0x77_u32.to_be(),
293 0xaa_u32.to_be(),
294 0x12_u32.to_be(),
295 0x34_u32.to_be(),
296 0x56_u32.to_be(),
297 0x7a_u32.to_be(),
298 0xa9_u32.to_be(),
299 0x09_u32.to_be(),
300 ])
301 );
302 }
303
304 #[test]
305 fn test_roundtrip() {
306 const HEX: &str = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
307 assert_eq!(hex::encode(Digest::from_hex(HEX).unwrap()), HEX);
308 }
309}