1use core::fmt;
2use std::str::FromStr;
3
4use arbitrary::Arbitrary;
5use bfieldcodec_derive::BFieldCodec;
6use get_size2::GetSize;
7use itertools::Itertools;
8use num_bigint::BigUint;
9use num_traits::ConstZero;
10use num_traits::Zero;
11use rand::Rng;
12use rand::RngExt;
13use rand::distr::Distribution;
14use rand::distr::StandardUniform;
15use serde::Deserialize;
16use serde::Deserializer;
17use serde::Serialize;
18use serde::Serializer;
19
20use crate::error::TryFromDigestError;
21use crate::error::TryFromHexDigestError;
22use crate::math::b_field_element::BFieldElement;
23use crate::prelude::Tip5;
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, GetSize, BFieldCodec, Arbitrary)]
29pub struct Digest(pub [BFieldElement; Digest::LEN]);
30
31impl PartialOrd for Digest {
32 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
33 Some(self.cmp(other))
34 }
35}
36
37impl Ord for Digest {
38 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
39 let Digest(self_inner) = self;
40 let Digest(other_inner) = other;
41 let self_as_u64s = self_inner.iter().rev().map(|bfe| bfe.value());
42 let other_as_u64s = other_inner.iter().rev().map(|bfe| bfe.value());
43 self_as_u64s.cmp(other_as_u64s)
44 }
45}
46
47impl Digest {
48 pub const LEN: usize = 5;
50
51 pub const BYTES: usize = Self::LEN * BFieldElement::BYTES;
53
54 pub(crate) const ALL_ZERO: Self = Self([BFieldElement::ZERO; Self::LEN]);
56
57 pub const fn values(self) -> [BFieldElement; Self::LEN] {
58 self.0
59 }
60
61 pub const fn new(digest: [BFieldElement; Self::LEN]) -> Self {
62 Self(digest)
63 }
64
65 pub const fn reversed(self) -> Digest {
68 let Digest([d0, d1, d2, d3, d4]) = self;
69 Digest([d4, d3, d2, d1, d0])
70 }
71}
72
73impl Default for Digest {
74 fn default() -> Self {
75 Self::ALL_ZERO
76 }
77}
78
79impl fmt::Display for Digest {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 write!(f, "{}", self.0.map(|elem| elem.to_string()).join(","))
82 }
83}
84
85impl fmt::LowerHex for Digest {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 let bytes = <[u8; Self::BYTES]>::from(*self);
88 write!(f, "{}", hex::encode(bytes))
89 }
90}
91
92impl fmt::UpperHex for Digest {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 let bytes = <[u8; Self::BYTES]>::from(*self);
95 write!(f, "{}", hex::encode_upper(bytes))
96 }
97}
98
99impl Distribution<Digest> for StandardUniform {
100 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Digest {
101 Digest::new(rng.random())
102 }
103}
104
105impl FromStr for Digest {
106 type Err = TryFromDigestError;
107
108 fn from_str(string: &str) -> Result<Self, Self::Err> {
109 let bfes: Vec<_> = string
110 .split(',')
111 .map(str::parse::<BFieldElement>)
112 .try_collect()?;
113 let invalid_len_err = Self::Err::InvalidLength(bfes.len());
114 let digest_innards = bfes.try_into().map_err(|_| invalid_len_err)?;
115
116 Ok(Digest(digest_innards))
117 }
118}
119
120impl TryFrom<&[BFieldElement]> for Digest {
121 type Error = TryFromDigestError;
122
123 fn try_from(value: &[BFieldElement]) -> Result<Self, Self::Error> {
124 let len = value.len();
125 let maybe_digest = value.try_into().map(Digest::new);
126 maybe_digest.map_err(|_| Self::Error::InvalidLength(len))
127 }
128}
129
130impl TryFrom<Vec<BFieldElement>> for Digest {
131 type Error = TryFromDigestError;
132
133 fn try_from(value: Vec<BFieldElement>) -> Result<Self, Self::Error> {
134 Digest::try_from(&value as &[BFieldElement])
135 }
136}
137
138impl From<Digest> for Vec<BFieldElement> {
139 fn from(val: Digest) -> Self {
140 val.0.to_vec()
141 }
142}
143
144impl From<Digest> for [u8; Digest::BYTES] {
145 fn from(Digest(innards): Digest) -> Self {
146 innards
147 .map(<[u8; BFieldElement::BYTES]>::from)
148 .concat()
149 .try_into()
150 .unwrap()
151 }
152}
153
154impl TryFrom<[u8; Digest::BYTES]> for Digest {
155 type Error = TryFromDigestError;
156
157 fn try_from(item: [u8; Self::BYTES]) -> Result<Self, Self::Error> {
158 let digest_innards: Vec<_> = item
159 .as_chunks::<{ BFieldElement::BYTES }>()
160 .0
161 .iter()
162 .map(|&bytes| BFieldElement::try_from(bytes))
163 .try_collect()?;
164
165 Ok(Self(digest_innards.try_into().unwrap()))
166 }
167}
168
169impl TryFrom<&[u8]> for Digest {
170 type Error = TryFromDigestError;
171
172 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
173 let array = <[u8; Self::BYTES]>::try_from(slice)
174 .map_err(|_e| TryFromDigestError::InvalidLength(slice.len()))?;
175 Self::try_from(array)
176 }
177}
178
179impl TryFrom<BigUint> for Digest {
180 type Error = TryFromDigestError;
181
182 fn try_from(value: BigUint) -> Result<Self, Self::Error> {
183 let mut remaining = value;
184 let mut digest_innards = [BFieldElement::ZERO; Self::LEN];
185 let modulus: BigUint = BFieldElement::P.into();
186 for digest_element in digest_innards.iter_mut() {
187 let element = u64::try_from(remaining.clone() % modulus.clone()).unwrap();
188 *digest_element = BFieldElement::new(element);
189 remaining /= modulus.clone();
190 }
191
192 if !remaining.is_zero() {
193 return Err(Self::Error::Overflow);
194 }
195
196 Ok(Digest::new(digest_innards))
197 }
198}
199
200impl From<Digest> for BigUint {
201 fn from(digest: Digest) -> Self {
202 let Digest(digest_innards) = digest;
203 let mut ret = BigUint::zero();
204 let modulus: BigUint = BFieldElement::P.into();
205 for i in (0..Digest::LEN).rev() {
206 ret *= modulus.clone();
207 let digest_element: BigUint = digest_innards[i].value().into();
208 ret += digest_element;
209 }
210
211 ret
212 }
213}
214
215impl Digest {
216 pub fn hash(self) -> Digest {
229 Tip5::hash_pair(self, Self::ALL_ZERO)
230 }
231
232 pub fn to_hex(self) -> String {
240 format!("{self:x}")
241 }
242
243 pub fn try_from_hex(data: impl AsRef<[u8]>) -> Result<Self, TryFromHexDigestError> {
245 let slice = hex::decode(data)?;
246 Ok(Self::try_from(&slice as &[u8])?)
247 }
248}
249
250impl Serialize for Digest {
253 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
254 if serializer.is_human_readable() {
255 self.to_hex().serialize(serializer)
256 } else {
257 self.0.serialize(serializer)
258 }
259 }
260}
261
262impl<'de> Deserialize<'de> for Digest {
265 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
266 where
267 D: Deserializer<'de>,
268 {
269 if deserializer.is_human_readable() {
270 let hex_string = String::deserialize(deserializer)?;
271 Self::try_from_hex(hex_string).map_err(serde::de::Error::custom)
272 } else {
273 <[_; _]>::deserialize(deserializer).map(Self::new)
274 }
275 }
276}
277
278#[cfg(test)]
279#[cfg_attr(coverage_nightly, coverage(off))]
280pub(crate) mod tests {
281 use num_traits::One;
282 use proptest::collection::vec;
283 use proptest::prelude::Arbitrary as ProptestArbitrary;
284 use proptest::prelude::*;
285 use proptest_arbitrary_adapter::arb;
286
287 use super::*;
288 use crate::error::ParseBFieldElementError;
289 use crate::prelude::*;
290 use crate::tests::proptest;
291 use crate::tests::test;
292
293 impl ProptestArbitrary for Digest {
294 type Parameters = ();
295 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
296 arb().prop_map(|d| d).no_shrink().boxed()
297 }
298
299 type Strategy = BoxedStrategy<Self>;
300 }
301
302 #[derive(Debug, Clone, PartialEq, Eq, test_strategy::Arbitrary)]
304 pub(crate) struct DigestCorruptor {
305 #[strategy(vec(0..Digest::LEN, 1..=Digest::LEN))]
306 #[filter(#corrupt_indices.iter().all_unique())]
307 corrupt_indices: Vec<usize>,
308
309 #[strategy(vec(arb(), #corrupt_indices.len()))]
310 corrupt_elements: Vec<BFieldElement>,
311 }
312
313 impl DigestCorruptor {
314 pub fn corrupt_digest(&self, digest: Digest) -> Result<Digest, TestCaseError> {
315 let mut corrupt_digest = digest;
316 for (&i, &element) in self.corrupt_indices.iter().zip(&self.corrupt_elements) {
317 corrupt_digest.0[i] = element;
318 }
319 if corrupt_digest == digest {
320 let reject_reason = "corruption must change digest".into();
321 return Err(TestCaseError::Reject(reject_reason));
322 }
323
324 Ok(corrupt_digest)
325 }
326 }
327
328 #[macro_rules_attr::apply(test)]
329 fn digest_corruptor_rejects_uncorrupting_corruption() {
330 let digest = Digest(bfe_array![1, 2, 3, 4, 5]);
331 let corruptor = DigestCorruptor {
332 corrupt_indices: vec![0],
333 corrupt_elements: bfe_vec![1],
334 };
335 let err = corruptor.corrupt_digest(digest).unwrap_err();
336 assert!(matches!(err, TestCaseError::Reject(_)));
337 }
338
339 #[macro_rules_attr::apply(test)]
340 fn display_is_as_expected() {
341 let digest = Digest::new(bfe_array![1, 2, 3, 4, 5]);
342 assert_eq!("1,2,3,4,5", format!("{digest}"));
343
344 let hex_digest =
345 "01000000000000000200000000000000030000000000000004000000000000000500000000000000";
346 assert_eq!(hex_digest, format!("{digest:x}"));
347 }
348
349 #[macro_rules_attr::apply(test)]
350 fn get_size() {
351 let stack = Digest::get_stack_size();
352
353 let bfes = bfe_array![12, 24, 36, 48, 60];
354 let tip5_digest_type_from_array: Digest = Digest::new(bfes);
355 let heap = tip5_digest_type_from_array.get_heap_size();
356 let total = tip5_digest_type_from_array.get_size();
357 println!("stack: {stack} + heap: {heap} = {total}");
358
359 assert_eq!(stack + heap, total)
360 }
361
362 #[macro_rules_attr::apply(test)]
363 fn digest_from_str() {
364 let valid_digest_string = "12063201067205522823,\
365 1529663126377206632,\
366 2090171368883726200,\
367 12975872837767296928,\
368 11492877804687889759";
369 let valid_digest = Digest::from_str(valid_digest_string);
370 assert!(valid_digest.is_ok());
371
372 let invalid_digest_string = "00059361073062755064,05168490802189810700";
373 let invalid_digest = Digest::from_str(invalid_digest_string);
374 assert!(invalid_digest.is_err());
375
376 let second_invalid_digest_string = "this_is_not_a_bfield_element,05168490802189810700";
377 let second_invalid_digest = Digest::from_str(second_invalid_digest_string);
378 assert!(second_invalid_digest.is_err());
379 }
380
381 #[macro_rules_attr::apply(proptest)]
382 fn test_reversed_involution(digest: Digest) {
383 prop_assert_eq!(digest, digest.reversed().reversed())
384 }
385
386 #[macro_rules_attr::apply(test)]
387 fn digest_biguint_conversion_simple_test() {
388 let fourteen: BigUint = 14u128.into();
389 let fourteen_converted_expected = Digest(bfe_array![14, 0, 0, 0, 0]);
390
391 let bfe_max: BigUint = BFieldElement::MAX.into();
392 let bfe_max_converted_expected = Digest(bfe_array![BFieldElement::MAX, 0, 0, 0, 0]);
393
394 let bfe_max_plus_one: BigUint = BFieldElement::P.into();
395 let bfe_max_plus_one_converted_expected = Digest(bfe_array![0, 1, 0, 0, 0]);
396
397 let two_pow_64: BigUint = (1u128 << 64).into();
398 let two_pow_64_converted_expected = Digest(bfe_array![(1u64 << 32) - 1, 1, 0, 0, 0]);
399
400 let two_pow_123: BigUint = (1u128 << 123).into();
401 let two_pow_123_converted_expected =
402 Digest([18446744069280366593, 576460752437641215, 0, 0, 0].map(BFieldElement::new));
403
404 let two_pow_315: BigUint = BigUint::from(2u128).pow(315);
405
406 let two_pow_315_converted_expected = Digest(bfe_array![
408 18446744069280366593_u64,
409 1729382257312923647_u64,
410 13258597298683772929_u64,
411 3458764513015234559_u64,
412 576460752840294400_u64,
413 ]);
414
415 assert_eq!(
417 fourteen_converted_expected,
418 fourteen.clone().try_into().unwrap()
419 );
420 assert_eq!(
421 bfe_max_converted_expected,
422 bfe_max.clone().try_into().unwrap()
423 );
424 assert_eq!(
425 bfe_max_plus_one_converted_expected,
426 bfe_max_plus_one.clone().try_into().unwrap()
427 );
428 assert_eq!(
429 two_pow_64_converted_expected,
430 two_pow_64.clone().try_into().unwrap()
431 );
432 assert_eq!(
433 two_pow_123_converted_expected,
434 two_pow_123.clone().try_into().unwrap()
435 );
436 assert_eq!(
437 two_pow_315_converted_expected,
438 two_pow_315.clone().try_into().unwrap()
439 );
440
441 assert_eq!(fourteen, fourteen_converted_expected.into());
443 assert_eq!(bfe_max, bfe_max_converted_expected.into());
444 assert_eq!(bfe_max_plus_one, bfe_max_plus_one_converted_expected.into());
445 assert_eq!(two_pow_64, two_pow_64_converted_expected.into());
446 assert_eq!(two_pow_123, two_pow_123_converted_expected.into());
447 assert_eq!(two_pow_315, two_pow_315_converted_expected.into());
448 }
449
450 #[macro_rules_attr::apply(proptest)]
451 fn digest_biguint_conversion_pbt(components_0: [u64; 4], component_1: u32) {
452 let big_uint = components_0
453 .into_iter()
454 .fold(BigUint::one(), |acc, x| acc * x);
455 let big_uint = big_uint * component_1;
456
457 let as_digest: Digest = big_uint.clone().try_into().unwrap();
458 let big_uint_again: BigUint = as_digest.into();
459 prop_assert_eq!(big_uint, big_uint_again);
460 }
461
462 #[macro_rules_attr::apply(test)]
463 fn digest_ordering() {
464 let val0 = Digest::new(bfe_array![0; Digest::LEN]);
465 let val1 = Digest::new(bfe_array![14, 0, 0, 0, 0]);
466 assert!(val1 > val0);
467
468 let val2 = Digest::new(bfe_array![14; Digest::LEN]);
469 assert!(val2 > val1);
470 assert!(val2 > val0);
471
472 let val3 = Digest::new(bfe_array![15, 14, 14, 14, 14]);
473 assert!(val3 > val2);
474 assert!(val3 > val1);
475 assert!(val3 > val0);
476
477 let val4 = Digest::new(bfe_array![14, 15, 14, 14, 14]);
478 assert!(val4 > val3);
479 assert!(val4 > val2);
480 assert!(val4 > val1);
481 assert!(val4 > val0);
482 }
483
484 #[macro_rules_attr::apply(test)]
485 fn digest_biguint_overflow_test() {
486 let mut two_pow_384: BigUint = (1u128 << 96).into();
487 two_pow_384 = two_pow_384.pow(4);
488 let err = Digest::try_from(two_pow_384).unwrap_err();
489
490 assert_eq!(TryFromDigestError::Overflow, err);
491 }
492
493 #[macro_rules_attr::apply(proptest)]
494 fn digest_to_bfe_vector_involution(digest: Digest) {
495 let bfes = <Vec<BFieldElement>>::from(digest);
496 let digest_again = Digest::try_from(bfes)?;
497 prop_assert_eq!(digest, digest_again);
498 }
499
500 #[macro_rules_attr::apply(proptest)]
501 fn bfe_vector_of_incorrect_length_cannot_become_a_digest(
502 #[filter(#bfes.len() != Digest::LEN)] bfes: Vec<BFieldElement>,
503 ) {
504 let bfes_len = bfes.len();
505 let Err(TryFromDigestError::InvalidLength(len)) = Digest::try_from(bfes) else {
506 return Err(TestCaseError::Fail("expected an error".into()));
507 };
508 prop_assert_eq!(bfes_len, len);
509 }
510
511 #[macro_rules_attr::apply(proptest)]
512 fn forty_bytes_can_be_converted_to_digest(bytes: [u8; Digest::BYTES]) {
513 let digest = Digest::try_from(bytes).unwrap();
514 let bytes_again: [u8; Digest::BYTES] = digest.into();
515 prop_assert_eq!(bytes, bytes_again);
516 }
517
518 #[macro_rules_attr::apply(test)]
520 fn try_from_bytes_not_canonical() -> Result<(), TryFromDigestError> {
521 let bytes: [u8; Digest::BYTES] = [255; Digest::BYTES];
522
523 assert!(Digest::try_from(bytes).is_err_and(|e| matches!(
524 e,
525 TryFromDigestError::InvalidBFieldElement(ParseBFieldElementError::NotCanonical(_))
526 )));
527
528 Ok(())
529 }
530
531 #[macro_rules_attr::apply(test)]
533 fn from_str_not_canonical() -> Result<(), TryFromDigestError> {
534 let str = format!("0,0,0,0,{}", u64::MAX);
535
536 assert!(Digest::from_str(&str).is_err_and(|e| matches!(
537 e,
538 TryFromDigestError::InvalidBFieldElement(ParseBFieldElementError::NotCanonical(_))
539 )));
540
541 Ok(())
542 }
543
544 #[macro_rules_attr::apply(test)]
545 fn bytes_in_matches_bytes_out() -> Result<(), TryFromDigestError> {
546 let bytes1: [u8; Digest::BYTES] = [254; Digest::BYTES];
547 let d1 = Digest::try_from(bytes1)?;
548
549 let bytes2: [u8; Digest::BYTES] = d1.into();
550 let d2 = Digest::try_from(bytes2)?;
551
552 assert_eq!(d1, d2);
553 assert_eq!(bytes1, bytes2);
554
555 Ok(())
556 }
557
558 #[macro_rules_attr::apply(proptest)]
559 fn any_digest_can_be_hashed(digest: Digest) {
560 digest.hash();
561 }
562
563 mod hex_test {
564 use super::*;
565 use crate::tests::proptest;
566 use crate::tests::test;
567
568 pub(super) fn hex_examples() -> Vec<(Digest, &'static str)> {
569 vec![
570 (
571 Digest::default(),
572 concat!(
573 "0000000000000000000000000000000000000000",
574 "0000000000000000000000000000000000000000"
575 ),
576 ),
577 (
578 Digest::new(bfe_array![0, 1, 10, 15, 255]),
579 concat!(
580 "000000000000000001000000000000000a000000",
581 "000000000f00000000000000ff00000000000000"
582 ),
583 ),
584 ]
591 }
592
593 #[macro_rules_attr::apply(test)]
594 fn digest_to_hex() {
595 for (digest, hex) in hex_examples() {
596 assert_eq!(&digest.to_hex(), hex);
597 }
598 }
599
600 #[macro_rules_attr::apply(proptest)]
601 fn to_hex_and_from_hex_are_reciprocal_proptest(bytes: [u8; Digest::BYTES]) {
602 let digest = Digest::try_from(bytes).unwrap();
603 let hex = digest.to_hex();
604 let digest_again = Digest::try_from_hex(&hex).unwrap();
605 let hex_again = digest_again.to_hex();
606 prop_assert_eq!(digest, digest_again);
607 prop_assert_eq!(hex, hex_again);
608
609 let lower_hex = format!("{digest:x}");
610 let digest_from_lower_hex = Digest::try_from_hex(lower_hex).unwrap();
611 prop_assert_eq!(digest, digest_from_lower_hex);
612
613 let upper_hex = format!("{digest:X}");
614 let digest_from_upper_hex = Digest::try_from_hex(upper_hex).unwrap();
615 prop_assert_eq!(digest, digest_from_upper_hex);
616 }
617
618 #[macro_rules_attr::apply(test)]
619 fn to_hex_and_from_hex_are_reciprocal() -> Result<(), TryFromHexDigestError> {
620 let hex_vals = vec![
621 "00000000000000000000000000000000000000000000000000000000000000000000000000000000",
622 "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
623 "0000000000000000000000000000000000000000000000000000000000000000000000000000000f",
624 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
625 ];
627 for hex in hex_vals {
628 let digest = Digest::try_from_hex(hex)?;
629 assert_eq!(hex, &digest.to_hex())
630 }
631 Ok(())
632 }
633
634 #[macro_rules_attr::apply(test)]
635 fn digest_from_hex() -> Result<(), TryFromHexDigestError> {
636 for (digest, hex) in hex_examples() {
637 assert_eq!(digest, Digest::try_from_hex(hex)?);
638 }
639
640 Ok(())
641 }
642
643 #[macro_rules_attr::apply(test)]
644 fn digest_from_invalid_hex_errors() {
645 use hex::FromHexError;
646
647 assert!(Digest::try_from_hex("taco").is_err_and(|e| matches!(
648 e,
649 TryFromHexDigestError::HexDecode(FromHexError::InvalidHexCharacter { .. })
650 )));
651
652 assert!(Digest::try_from_hex("0").is_err_and(|e| matches!(
653 e,
654 TryFromHexDigestError::HexDecode(FromHexError::OddLength)
655 )));
656
657 assert!(Digest::try_from_hex("00").is_err_and(|e| matches!(
658 e,
659 TryFromHexDigestError::Digest(TryFromDigestError::InvalidLength(_))
660 )));
661
662 assert!(Digest::try_from_hex(
664 "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
665 )
666 .is_err_and(|e| matches!(
667 e,
668 TryFromHexDigestError::Digest(TryFromDigestError::InvalidBFieldElement(
669 ParseBFieldElementError::NotCanonical(_)
670 ))
671 )));
672 }
673 }
674
675 mod serde_test {
676 use super::hex_test::hex_examples;
677 use super::*;
678
679 mod json_test {
680 use super::*;
681 use crate::tests::test;
682
683 #[macro_rules_attr::apply(test)]
684 fn serialize() -> Result<(), serde_json::Error> {
685 for (digest, hex) in hex_examples() {
686 assert_eq!(serde_json::to_string(&digest)?, format!("\"{hex}\""));
687 }
688 Ok(())
689 }
690
691 #[macro_rules_attr::apply(test)]
692 fn deserialize() -> Result<(), serde_json::Error> {
693 for (digest, hex) in hex_examples() {
694 let json_hex = format!("\"{hex}\"");
695 let digest_deserialized: Digest = serde_json::from_str::<Digest>(&json_hex)?;
696 assert_eq!(digest_deserialized, digest);
697 }
698 Ok(())
699 }
700 }
701
702 mod bincode_test {
703 use super::*;
704 use crate::tests::test;
705
706 fn bincode_examples() -> Vec<(Digest, [u8; Digest::BYTES])> {
707 vec![
708 (Digest::default(), [0u8; Digest::BYTES]),
709 (
710 Digest::new(bfe_array![0, 1, 10, 15, 255]),
711 [
712 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0,
713 0, 15, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
714 ],
715 ),
716 ]
717 }
718
719 #[macro_rules_attr::apply(test)]
720 fn serialize() {
721 for (digest, bytes) in bincode_examples() {
722 assert_eq!(bincode::serialize(&digest).unwrap(), bytes);
723 }
724 }
725
726 #[macro_rules_attr::apply(test)]
727 fn deserialize() {
728 for (digest, bytes) in bincode_examples() {
729 assert_eq!(bincode::deserialize::<Digest>(&bytes).unwrap(), digest);
730 }
731 }
732 }
733 }
734}