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