massa_hash/
hash_xof.rs

1use std::ops::{BitXor, BitXorAssign};
2
3use massa_serialization::{Deserializer, SerializeError, Serializer};
4use nom::{
5    error::{context, ContextError, ParseError},
6    IResult,
7};
8
9use crate::Hash;
10
11/// Extended Hash
12#[derive(Clone, Copy, PartialEq, Eq)]
13pub struct HashXof<const SIZE: usize>(pub [u8; SIZE]);
14
15impl<const SIZE: usize> HashXof<SIZE> {
16    /// From bytes
17    pub fn from_bytes(bytes: &[u8; SIZE]) -> Self {
18        HashXof(*bytes)
19    }
20
21    /// Transform into bytes
22    pub fn to_bytes(&self) -> &[u8; SIZE] {
23        &self.0
24    }
25
26    /// Compute from raw data
27    pub fn compute_from(data: &[u8]) -> HashXof<SIZE> {
28        let mut hasher = blake3::Hasher::new();
29        hasher.update(data);
30        let mut hash = [0u8; SIZE];
31        let mut output_reader = hasher.finalize_xof();
32        output_reader.fill(&mut hash);
33        HashXof(hash)
34    }
35
36    /// Compute from tuple of byte arrays
37    pub fn compute_from_tuple(data: &[&[u8]]) -> Self {
38        let mut hasher = blake3::Hasher::new();
39        for d in data {
40            hasher.update(&(d.len() as u64).to_be_bytes());
41            hasher.update(d);
42        }
43        let mut hash = [0u8; SIZE];
44        let mut output_reader = hasher.finalize_xof();
45        output_reader.fill(&mut hash);
46        HashXof(hash)
47    }
48
49    /// Serialize a Hash using `bs58` encoding with checksum.
50    /// Motivations for using base58 encoding:
51    ///
52    /// base58_check is like base64 but-
53    /// * fully standardized (no = vs /)
54    /// * no weird characters (eg. +) only alphanumeric
55    /// * ambiguous letters combined (eg. O vs 0, or l vs 1)
56    /// * contains a checksum at the end to detect typing errors
57    ///    
58    pub fn to_bs58_check(&self) -> String {
59        bs58::encode(self.0).with_check().into_string()
60    }
61}
62
63// To use this xor operator you must ensure that you have all the criteria listed here :
64// https://github.com/massalabs/massa/discussions/3852#discussioncomment-6188158
65impl<const SIZE: usize> BitXorAssign for HashXof<SIZE> {
66    fn bitxor_assign(&mut self, rhs: Self) {
67        *self = *self ^ rhs;
68    }
69}
70
71// To use this xor operator you must ensure that you have all the criteria listed here :
72// https://github.com/massalabs/massa/discussions/3852#discussioncomment-6188158
73impl<const SIZE: usize> BitXor for HashXof<SIZE> {
74    type Output = Self;
75
76    fn bitxor(self, other: Self) -> Self {
77        let xored = self
78            .0
79            .iter()
80            .zip(other.0.iter())
81            .map(|(a, b)| a ^ b)
82            .collect::<Vec<u8>>()
83            .try_into()
84            .unwrap();
85        HashXof(xored)
86    }
87}
88
89impl<const SIZE: usize> std::fmt::Display for HashXof<SIZE> {
90    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
91        write!(f, "{}", Hash::compute_from(self.to_bytes()).to_bs58_check())
92    }
93}
94
95impl<const SIZE: usize> std::fmt::Debug for HashXof<SIZE> {
96    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
97        write!(f, "{}", self.to_bs58_check())
98    }
99}
100
101/// Serializer for `HashXof`
102#[derive(Default, Clone)]
103pub struct HashXofSerializer;
104
105impl HashXofSerializer {
106    /// Creates a serializer for `HashXof`
107    pub const fn new() -> Self {
108        Self
109    }
110}
111
112impl<const SIZE: usize> Serializer<HashXof<SIZE>> for HashXofSerializer {
113    fn serialize(&self, value: &HashXof<SIZE>, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
114        buffer.extend(value.to_bytes());
115        Ok(())
116    }
117}
118
119/// Deserializer for `HashXof`
120#[derive(Default, Clone)]
121pub struct HashXofDeserializer;
122
123impl HashXofDeserializer {
124    /// Creates a deserializer for `HashXof`
125    pub const fn new() -> Self {
126        Self
127    }
128}
129
130impl<const SIZE: usize> Deserializer<HashXof<SIZE>> for HashXofDeserializer {
131    /// ## Example
132    /// ```rust
133    /// use massa_hash::{HashXof, HASH_XOF_SIZE_BYTES, HashXofDeserializer};
134    /// use massa_serialization::{Serializer, Deserializer, DeserializeError};
135    ///
136    /// let hash_deserializer = HashXofDeserializer::new();
137    /// let hash: HashXof<HASH_XOF_SIZE_BYTES> = HashXof::compute_from(&"hello world".as_bytes());
138    /// let (rest, deserialized) = hash_deserializer.deserialize::<DeserializeError>(hash.to_bytes()).unwrap();
139    /// assert_eq!(deserialized, hash);
140    /// assert_eq!(rest.len(), 0);
141    /// ```
142    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
143        &self,
144        buffer: &'a [u8],
145    ) -> IResult<&'a [u8], HashXof<SIZE>, E> {
146        context("Failed hashxof deserialization", |input: &'a [u8]| {
147            if buffer.len() < SIZE {
148                return Err(nom::Err::Error(ParseError::from_error_kind(
149                    input,
150                    nom::error::ErrorKind::LengthValue,
151                )));
152            }
153            Ok((
154                &buffer[SIZE..],
155                HashXof::from_bytes(&buffer[..SIZE].try_into().map_err(|_| {
156                    nom::Err::Error(ParseError::from_error_kind(
157                        input,
158                        nom::error::ErrorKind::Fail,
159                    ))
160                })?),
161            ))
162        })(buffer)
163    }
164}