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#[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 pub const LEN: usize = 5;
49
50 pub const BYTES: usize = Self::LEN * BFieldElement::BYTES;
52
53 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 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 pub fn hash(self) -> Digest {
227 Tip5::hash_pair(self, Self::ALL_ZERO)
228 }
229
230 pub fn to_hex(self) -> String {
238 format!("{self:x}")
239 }
240
241 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
248impl 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
260impl<'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 #[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 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 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 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 #[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 #[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 ]
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 ];
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 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}