twenty_first/tip5/
digest.rs

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::distr::Distribution;
13use rand::distr::StandardUniform;
14use serde::Deserialize;
15use serde::Deserializer;
16use serde::Serialize;
17use serde::Serializer;
18
19use crate::error::TryFromDigestError;
20use crate::error::TryFromHexDigestError;
21use crate::math::b_field_element::BFieldElement;
22use crate::prelude::Tip5;
23
24/// The result of hashing a sequence of elements, for example using [Tip5].
25/// Sometimes called a “hash”.
26// note: Serialize and Deserialize have custom implementations below
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, GetSize, BFieldCodec, Arbitrary)]
28pub struct Digest(pub [BFieldElement; Digest::LEN]);
29
30impl PartialOrd for Digest {
31    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
32        Some(self.cmp(other))
33    }
34}
35
36impl Ord for Digest {
37    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
38        let Digest(self_inner) = self;
39        let Digest(other_inner) = other;
40        let self_as_u64s = self_inner.iter().rev().map(|bfe| bfe.value());
41        let other_as_u64s = other_inner.iter().rev().map(|bfe| bfe.value());
42        self_as_u64s.cmp(other_as_u64s)
43    }
44}
45
46impl Digest {
47    /// The number of [elements](BFieldElement) in a digest.
48    pub const LEN: usize = 5;
49
50    /// The number of bytes in a digest.
51    pub const BYTES: usize = Self::LEN * BFieldElement::BYTES;
52
53    /// The all-zero digest.
54    pub(crate) const ALL_ZERO: Self = Self([BFieldElement::ZERO; Self::LEN]);
55
56    pub const fn values(self) -> [BFieldElement; Self::LEN] {
57        self.0
58    }
59
60    pub const fn new(digest: [BFieldElement; Self::LEN]) -> Self {
61        Self(digest)
62    }
63
64    /// Returns a new digest but whose elements are reversed relative to self.
65    /// This function is an involutive endomorphism.
66    pub const fn reversed(self) -> Digest {
67        let Digest([d0, d1, d2, d3, d4]) = self;
68        Digest([d4, d3, d2, d1, d0])
69    }
70}
71
72impl Default for Digest {
73    fn default() -> Self {
74        Self::ALL_ZERO
75    }
76}
77
78impl fmt::Display for Digest {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        write!(f, "{}", self.0.map(|elem| elem.to_string()).join(","))
81    }
82}
83
84impl fmt::LowerHex for Digest {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        let bytes = <[u8; Self::BYTES]>::from(*self);
87        write!(f, "{}", hex::encode(bytes))
88    }
89}
90
91impl fmt::UpperHex for Digest {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        let bytes = <[u8; Self::BYTES]>::from(*self);
94        write!(f, "{}", hex::encode_upper(bytes))
95    }
96}
97
98impl Distribution<Digest> for StandardUniform {
99    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Digest {
100        Digest::new(rng.random())
101    }
102}
103
104impl FromStr for Digest {
105    type Err = TryFromDigestError;
106
107    fn from_str(string: &str) -> Result<Self, Self::Err> {
108        let bfes: Vec<_> = string
109            .split(',')
110            .map(str::parse::<BFieldElement>)
111            .try_collect()?;
112        let invalid_len_err = Self::Err::InvalidLength(bfes.len());
113        let digest_innards = bfes.try_into().map_err(|_| invalid_len_err)?;
114
115        Ok(Digest(digest_innards))
116    }
117}
118
119impl TryFrom<&[BFieldElement]> for Digest {
120    type Error = TryFromDigestError;
121
122    fn try_from(value: &[BFieldElement]) -> Result<Self, Self::Error> {
123        let len = value.len();
124        let maybe_digest = value.try_into().map(Digest::new);
125        maybe_digest.map_err(|_| Self::Error::InvalidLength(len))
126    }
127}
128
129impl TryFrom<Vec<BFieldElement>> for Digest {
130    type Error = TryFromDigestError;
131
132    fn try_from(value: Vec<BFieldElement>) -> Result<Self, Self::Error> {
133        Digest::try_from(&value as &[BFieldElement])
134    }
135}
136
137impl From<Digest> for Vec<BFieldElement> {
138    fn from(val: Digest) -> Self {
139        val.0.to_vec()
140    }
141}
142
143impl From<Digest> for [u8; Digest::BYTES] {
144    fn from(Digest(innards): Digest) -> Self {
145        innards
146            .map(<[u8; BFieldElement::BYTES]>::from)
147            .concat()
148            .try_into()
149            .unwrap()
150    }
151}
152
153impl TryFrom<[u8; Digest::BYTES]> for Digest {
154    type Error = TryFromDigestError;
155
156    fn try_from(item: [u8; Self::BYTES]) -> Result<Self, Self::Error> {
157        let digest_innards: Vec<_> = item
158            .chunks_exact(BFieldElement::BYTES)
159            .map(BFieldElement::try_from)
160            .try_collect()?;
161
162        Ok(Self(digest_innards.try_into().unwrap()))
163    }
164}
165
166impl TryFrom<&[u8]> for Digest {
167    type Error = TryFromDigestError;
168
169    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
170        let array = <[u8; Self::BYTES]>::try_from(slice)
171            .map_err(|_e| TryFromDigestError::InvalidLength(slice.len()))?;
172        Self::try_from(array)
173    }
174}
175
176impl TryFrom<BigUint> for Digest {
177    type Error = TryFromDigestError;
178
179    fn try_from(value: BigUint) -> Result<Self, Self::Error> {
180        let mut remaining = value;
181        let mut digest_innards = [BFieldElement::ZERO; Self::LEN];
182        let modulus: BigUint = BFieldElement::P.into();
183        for digest_element in digest_innards.iter_mut() {
184            let element = u64::try_from(remaining.clone() % modulus.clone()).unwrap();
185            *digest_element = BFieldElement::new(element);
186            remaining /= modulus.clone();
187        }
188
189        if !remaining.is_zero() {
190            return Err(Self::Error::Overflow);
191        }
192
193        Ok(Digest::new(digest_innards))
194    }
195}
196
197impl From<Digest> for BigUint {
198    fn from(digest: Digest) -> Self {
199        let Digest(digest_innards) = digest;
200        let mut ret = BigUint::zero();
201        let modulus: BigUint = BFieldElement::P.into();
202        for i in (0..Digest::LEN).rev() {
203            ret *= modulus.clone();
204            let digest_element: BigUint = digest_innards[i].value().into();
205            ret += digest_element;
206        }
207
208        ret
209    }
210}
211
212impl Digest {
213    /// Hash this digest using [Tip5], producing a new digest.
214    ///
215    /// A digest can be used as a source of entropy. It can be beneficial or even
216    /// necessary to not reveal the entropy itself, but use it as the seed for
217    /// some deterministic computation. In such cases, hashing the digest using
218    /// this method is probably the right thing to do.
219    /// If the digest in question is used for its entropy only, there might not be a
220    /// known meaningful pre-image for that digest.
221    ///
222    /// This method invokes [`Tip5::hash_pair`] with the right operand being the
223    /// zero digest, agreeing with the standard way to hash a digest in the virtual
224    /// machine.
225    // todo: introduce a dedicated newtype for an entropy source
226    pub fn hash(self) -> Digest {
227        Tip5::hash_pair(self, Self::ALL_ZERO)
228    }
229
230    /// Encode digest as hex.
231    ///
232    /// Since `Digest` also implements [`LowerHex`][lo] and [`UpperHex`][up], it is
233    /// possible to `{:x}`-format directly, _e.g._, `print!("{digest:x}")`.
234    ///
235    /// [lo]: fmt::LowerHex
236    /// [up]: fmt::UpperHex
237    pub fn to_hex(self) -> String {
238        format!("{self:x}")
239    }
240
241    /// Decode hex string to [`Digest`]. Must not include leading “0x”.
242    pub fn try_from_hex(data: impl AsRef<[u8]>) -> Result<Self, TryFromHexDigestError> {
243        let slice = hex::decode(data)?;
244        Ok(Self::try_from(&slice as &[u8])?)
245    }
246}
247
248// we implement Serialize so that we can serialize as hex for human readable
249// formats like JSON but use default serializer for other formats likes bincode
250impl Serialize for Digest {
251    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
252        if serializer.is_human_readable() {
253            self.to_hex().serialize(serializer)
254        } else {
255            self.0.serialize(serializer)
256        }
257    }
258}
259
260// we impl Deserialize so that we can deserialize as hex for human readable
261// formats like JSON but use default deserializer for other formats like bincode
262impl<'de> Deserialize<'de> for Digest {
263    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
264    where
265        D: Deserializer<'de>,
266    {
267        if deserializer.is_human_readable() {
268            let hex_string = String::deserialize(deserializer)?;
269            Self::try_from_hex(hex_string).map_err(serde::de::Error::custom)
270        } else {
271            Ok(Self::new(<[BFieldElement; Self::LEN]>::deserialize(
272                deserializer,
273            )?))
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_interop::arb;
286    use test_strategy::proptest;
287
288    use super::*;
289    use crate::error::ParseBFieldElementError;
290    use crate::prelude::*;
291
292    impl ProptestArbitrary for Digest {
293        type Parameters = ();
294        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
295            arb().prop_map(|d| d).no_shrink().boxed()
296        }
297
298        type Strategy = BoxedStrategy<Self>;
299    }
300
301    /// Test helper struct for corrupting digests. Primarily used for negative tests.
302    #[derive(Debug, Clone, PartialEq, Eq, test_strategy::Arbitrary)]
303    pub(crate) struct DigestCorruptor {
304        #[strategy(vec(0..Digest::LEN, 1..=Digest::LEN))]
305        #[filter(#corrupt_indices.iter().all_unique())]
306        corrupt_indices: Vec<usize>,
307
308        #[strategy(vec(arb(), #corrupt_indices.len()))]
309        corrupt_elements: Vec<BFieldElement>,
310    }
311
312    impl DigestCorruptor {
313        pub fn corrupt_digest(&self, digest: Digest) -> Result<Digest, TestCaseError> {
314            let mut corrupt_digest = digest;
315            for (&i, &element) in self.corrupt_indices.iter().zip(&self.corrupt_elements) {
316                corrupt_digest.0[i] = element;
317            }
318            if corrupt_digest == digest {
319                let reject_reason = "corruption must change digest".into();
320                return Err(TestCaseError::Reject(reject_reason));
321            }
322
323            Ok(corrupt_digest)
324        }
325    }
326
327    #[test]
328    fn digest_corruptor_rejects_uncorrupting_corruption() {
329        let digest = Digest(bfe_array![1, 2, 3, 4, 5]);
330        let corruptor = DigestCorruptor {
331            corrupt_indices: vec![0],
332            corrupt_elements: bfe_vec![1],
333        };
334        let err = corruptor.corrupt_digest(digest).unwrap_err();
335        assert!(matches!(err, TestCaseError::Reject(_)));
336    }
337
338    #[test]
339    fn get_size() {
340        let stack = Digest::get_stack_size();
341
342        let bfes = bfe_array![12, 24, 36, 48, 60];
343        let tip5_digest_type_from_array: Digest = Digest::new(bfes);
344        let heap = tip5_digest_type_from_array.get_heap_size();
345        let total = tip5_digest_type_from_array.get_size();
346        println!("stack: {stack} + heap: {heap} = {total}");
347
348        assert_eq!(stack + heap, total)
349    }
350
351    #[test]
352    fn digest_from_str() {
353        let valid_digest_string = "12063201067205522823,\
354            1529663126377206632,\
355            2090171368883726200,\
356            12975872837767296928,\
357            11492877804687889759";
358        let valid_digest = Digest::from_str(valid_digest_string);
359        assert!(valid_digest.is_ok());
360
361        let invalid_digest_string = "00059361073062755064,05168490802189810700";
362        let invalid_digest = Digest::from_str(invalid_digest_string);
363        assert!(invalid_digest.is_err());
364
365        let second_invalid_digest_string = "this_is_not_a_bfield_element,05168490802189810700";
366        let second_invalid_digest = Digest::from_str(second_invalid_digest_string);
367        assert!(second_invalid_digest.is_err());
368    }
369
370    #[proptest]
371    fn test_reversed_involution(digest: Digest) {
372        prop_assert_eq!(digest, digest.reversed().reversed())
373    }
374
375    #[test]
376    fn digest_biguint_conversion_simple_test() {
377        let fourteen: BigUint = 14u128.into();
378        let fourteen_converted_expected = Digest(bfe_array![14, 0, 0, 0, 0]);
379
380        let bfe_max: BigUint = BFieldElement::MAX.into();
381        let bfe_max_converted_expected = Digest(bfe_array![BFieldElement::MAX, 0, 0, 0, 0]);
382
383        let bfe_max_plus_one: BigUint = BFieldElement::P.into();
384        let bfe_max_plus_one_converted_expected = Digest(bfe_array![0, 1, 0, 0, 0]);
385
386        let two_pow_64: BigUint = (1u128 << 64).into();
387        let two_pow_64_converted_expected = Digest(bfe_array![(1u64 << 32) - 1, 1, 0, 0, 0]);
388
389        let two_pow_123: BigUint = (1u128 << 123).into();
390        let two_pow_123_converted_expected =
391            Digest([18446744069280366593, 576460752437641215, 0, 0, 0].map(BFieldElement::new));
392
393        let two_pow_315: BigUint = BigUint::from(2u128).pow(315);
394
395        // Result calculated on Wolfram alpha
396        let two_pow_315_converted_expected = Digest(bfe_array![
397            18446744069280366593_u64,
398            1729382257312923647_u64,
399            13258597298683772929_u64,
400            3458764513015234559_u64,
401            576460752840294400_u64,
402        ]);
403
404        // Verify conversion from BigUint to Digest
405        assert_eq!(
406            fourteen_converted_expected,
407            fourteen.clone().try_into().unwrap()
408        );
409        assert_eq!(
410            bfe_max_converted_expected,
411            bfe_max.clone().try_into().unwrap()
412        );
413        assert_eq!(
414            bfe_max_plus_one_converted_expected,
415            bfe_max_plus_one.clone().try_into().unwrap()
416        );
417        assert_eq!(
418            two_pow_64_converted_expected,
419            two_pow_64.clone().try_into().unwrap()
420        );
421        assert_eq!(
422            two_pow_123_converted_expected,
423            two_pow_123.clone().try_into().unwrap()
424        );
425        assert_eq!(
426            two_pow_315_converted_expected,
427            two_pow_315.clone().try_into().unwrap()
428        );
429
430        // Verify conversion from Digest to BigUint
431        assert_eq!(fourteen, fourteen_converted_expected.into());
432        assert_eq!(bfe_max, bfe_max_converted_expected.into());
433        assert_eq!(bfe_max_plus_one, bfe_max_plus_one_converted_expected.into());
434        assert_eq!(two_pow_64, two_pow_64_converted_expected.into());
435        assert_eq!(two_pow_123, two_pow_123_converted_expected.into());
436        assert_eq!(two_pow_315, two_pow_315_converted_expected.into());
437    }
438
439    #[proptest]
440    fn digest_biguint_conversion_pbt(components_0: [u64; 4], component_1: u32) {
441        let big_uint = components_0
442            .into_iter()
443            .fold(BigUint::one(), |acc, x| acc * x);
444        let big_uint = big_uint * component_1;
445
446        let as_digest: Digest = big_uint.clone().try_into().unwrap();
447        let big_uint_again: BigUint = as_digest.into();
448        prop_assert_eq!(big_uint, big_uint_again);
449    }
450
451    #[test]
452    fn digest_ordering() {
453        let val0 = Digest::new(bfe_array![0; Digest::LEN]);
454        let val1 = Digest::new(bfe_array![14, 0, 0, 0, 0]);
455        assert!(val1 > val0);
456
457        let val2 = Digest::new(bfe_array![14; Digest::LEN]);
458        assert!(val2 > val1);
459        assert!(val2 > val0);
460
461        let val3 = Digest::new(bfe_array![15, 14, 14, 14, 14]);
462        assert!(val3 > val2);
463        assert!(val3 > val1);
464        assert!(val3 > val0);
465
466        let val4 = Digest::new(bfe_array![14, 15, 14, 14, 14]);
467        assert!(val4 > val3);
468        assert!(val4 > val2);
469        assert!(val4 > val1);
470        assert!(val4 > val0);
471    }
472
473    #[test]
474    fn digest_biguint_overflow_test() {
475        let mut two_pow_384: BigUint = (1u128 << 96).into();
476        two_pow_384 = two_pow_384.pow(4);
477        let err = Digest::try_from(two_pow_384).unwrap_err();
478
479        assert_eq!(TryFromDigestError::Overflow, err);
480    }
481
482    #[proptest]
483    fn forty_bytes_can_be_converted_to_digest(bytes: [u8; Digest::BYTES]) {
484        let digest = Digest::try_from(bytes).unwrap();
485        let bytes_again: [u8; Digest::BYTES] = digest.into();
486        prop_assert_eq!(bytes, bytes_again);
487    }
488
489    // note: for background on this test, see issue 195
490    #[test]
491    fn try_from_bytes_not_canonical() -> Result<(), TryFromDigestError> {
492        let bytes: [u8; Digest::BYTES] = [255; Digest::BYTES];
493
494        assert!(Digest::try_from(bytes).is_err_and(|e| matches!(
495            e,
496            TryFromDigestError::InvalidBFieldElement(ParseBFieldElementError::NotCanonical(_))
497        )));
498
499        Ok(())
500    }
501
502    // note: for background on this test, see issue 195
503    #[test]
504    fn from_str_not_canonical() -> Result<(), TryFromDigestError> {
505        let str = format!("0,0,0,0,{}", u64::MAX);
506
507        assert!(Digest::from_str(&str).is_err_and(|e| matches!(
508            e,
509            TryFromDigestError::InvalidBFieldElement(ParseBFieldElementError::NotCanonical(_))
510        )));
511
512        Ok(())
513    }
514
515    #[test]
516    fn bytes_in_matches_bytes_out() -> Result<(), TryFromDigestError> {
517        let bytes1: [u8; Digest::BYTES] = [254; Digest::BYTES];
518        let d1 = Digest::try_from(bytes1)?;
519
520        let bytes2: [u8; Digest::BYTES] = d1.into();
521        let d2 = Digest::try_from(bytes2)?;
522
523        assert_eq!(d1, d2);
524        assert_eq!(bytes1, bytes2);
525
526        Ok(())
527    }
528
529    mod hex_test {
530        use super::*;
531
532        pub(super) fn hex_examples() -> Vec<(Digest, &'static str)> {
533            vec![
534                (
535                    Digest::default(),
536                    concat!(
537                        "0000000000000000000000000000000000000000",
538                        "0000000000000000000000000000000000000000"
539                    ),
540                ),
541                (
542                    Digest::new(bfe_array![0, 1, 10, 15, 255]),
543                    concat!(
544                        "000000000000000001000000000000000a000000",
545                        "000000000f00000000000000ff00000000000000"
546                    ),
547                ),
548                // note: this would result in NotCanonical error. See issue 195
549                // (
550                //     Digest::new(bfe_array![0, 1, 10, 15, 255]),
551                //     concat!("ffffffffffffffffffffffffffffffffffffffff",
552                //             "ffffffffffffffffffffffffffffffffffffffff"),
553                // ),
554            ]
555        }
556
557        #[test]
558        fn digest_to_hex() {
559            for (digest, hex) in hex_examples() {
560                assert_eq!(&digest.to_hex(), hex);
561            }
562        }
563
564        #[proptest]
565        fn to_hex_and_from_hex_are_reciprocal_proptest(bytes: [u8; Digest::BYTES]) {
566            let digest = Digest::try_from(bytes).unwrap();
567            let hex = digest.to_hex();
568            let digest_again = Digest::try_from_hex(&hex).unwrap();
569            let hex_again = digest_again.to_hex();
570            prop_assert_eq!(digest, digest_again);
571            prop_assert_eq!(hex, hex_again);
572
573            let lower_hex = format!("{digest:x}");
574            let digest_from_lower_hex = Digest::try_from_hex(lower_hex).unwrap();
575            prop_assert_eq!(digest, digest_from_lower_hex);
576
577            let upper_hex = format!("{digest:X}");
578            let digest_from_upper_hex = Digest::try_from_hex(upper_hex).unwrap();
579            prop_assert_eq!(digest, digest_from_upper_hex);
580        }
581
582        #[test]
583        fn to_hex_and_from_hex_are_reciprocal() -> Result<(), TryFromHexDigestError> {
584            let hex_vals = vec![
585                "00000000000000000000000000000000000000000000000000000000000000000000000000000000",
586                "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
587                "0000000000000000000000000000000000000000000000000000000000000000000000000000000f",
588                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
589                // note: all "ff…ff" would result in NotCanonical error. See issue 195
590            ];
591            for hex in hex_vals {
592                let digest = Digest::try_from_hex(hex)?;
593                assert_eq!(hex, &digest.to_hex())
594            }
595            Ok(())
596        }
597
598        #[test]
599        fn digest_from_hex() -> Result<(), TryFromHexDigestError> {
600            for (digest, hex) in hex_examples() {
601                assert_eq!(digest, Digest::try_from_hex(hex)?);
602            }
603
604            Ok(())
605        }
606
607        #[test]
608        fn digest_from_invalid_hex_errors() {
609            use hex::FromHexError;
610
611            assert!(Digest::try_from_hex("taco").is_err_and(|e| matches!(
612                e,
613                TryFromHexDigestError::HexDecode(FromHexError::InvalidHexCharacter { .. })
614            )));
615
616            assert!(Digest::try_from_hex("0").is_err_and(|e| matches!(
617                e,
618                TryFromHexDigestError::HexDecode(FromHexError::OddLength)
619            )));
620
621            assert!(Digest::try_from_hex("00").is_err_and(|e| matches!(
622                e,
623                TryFromHexDigestError::Digest(TryFromDigestError::InvalidLength(_))
624            )));
625
626            // NotCanonical error. See issue 195
627            assert!(Digest::try_from_hex(
628                "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
629            )
630            .is_err_and(|e| matches!(
631                e,
632                TryFromHexDigestError::Digest(TryFromDigestError::InvalidBFieldElement(
633                    ParseBFieldElementError::NotCanonical(_)
634                ))
635            )));
636        }
637    }
638
639    mod serde_test {
640        use super::hex_test::hex_examples;
641        use super::*;
642
643        mod json_test {
644            use super::*;
645
646            #[test]
647            fn serialize() -> Result<(), serde_json::Error> {
648                for (digest, hex) in hex_examples() {
649                    assert_eq!(serde_json::to_string(&digest)?, format!("\"{hex}\""));
650                }
651                Ok(())
652            }
653
654            #[test]
655            fn deserialize() -> Result<(), serde_json::Error> {
656                for (digest, hex) in hex_examples() {
657                    let json_hex = format!("\"{hex}\"");
658                    let digest_deserialized: Digest = serde_json::from_str::<Digest>(&json_hex)?;
659                    assert_eq!(digest_deserialized, digest);
660                }
661                Ok(())
662            }
663        }
664
665        mod bincode_test {
666            use super::*;
667
668            fn bincode_examples() -> Vec<(Digest, [u8; Digest::BYTES])> {
669                vec![
670                    (Digest::default(), [0u8; Digest::BYTES]),
671                    (
672                        Digest::new(bfe_array![0, 1, 10, 15, 255]),
673                        [
674                            0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0,
675                            0, 15, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0,
676                        ],
677                    ),
678                ]
679            }
680
681            #[test]
682            fn serialize() {
683                for (digest, bytes) in bincode_examples() {
684                    assert_eq!(bincode::serialize(&digest).unwrap(), bytes);
685                }
686            }
687
688            #[test]
689            fn deserialize() {
690                for (digest, bytes) in bincode_examples() {
691                    assert_eq!(bincode::deserialize::<Digest>(&bytes).unwrap(), digest);
692                }
693            }
694        }
695    }
696}