iroh_blobs/
hash.rs

1//! The blake3 hash used in Iroh.
2
3use std::{borrow::Borrow, fmt, str::FromStr};
4
5use arrayvec::ArrayString;
6use bao_tree::blake3;
7use n0_snafu::SpanTrace;
8use nested_enum_utils::common_fields;
9use postcard::experimental::max_size::MaxSize;
10use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
11use snafu::{Backtrace, ResultExt, Snafu};
12
13use crate::store::util::DD;
14
15/// Hash type used throughout.
16#[derive(PartialEq, Eq, Copy, Clone, Hash)]
17pub struct Hash(blake3::Hash);
18
19impl fmt::Debug for Hash {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        f.debug_tuple("Hash").field(&DD(self.to_hex())).finish()
22    }
23}
24
25impl Hash {
26    /// The hash for the empty byte range (`b""`).
27    pub const EMPTY: Hash = Hash::from_bytes([
28        175, 19, 73, 185, 245, 249, 161, 166, 160, 64, 77, 234, 54, 220, 201, 73, 155, 203, 37,
29        201, 173, 193, 18, 183, 204, 154, 147, 202, 228, 31, 50, 98,
30    ]);
31
32    /// Calculate the hash of the provided bytes.
33    pub fn new(buf: impl AsRef<[u8]>) -> Self {
34        let val = blake3::hash(buf.as_ref());
35        Hash(val)
36    }
37
38    /// Bytes of the hash.
39    pub fn as_bytes(&self) -> &[u8; 32] {
40        self.0.as_bytes()
41    }
42
43    /// Create a `Hash` from its raw bytes representation.
44    pub const fn from_bytes(bytes: [u8; 32]) -> Self {
45        Self(blake3::Hash::from_bytes(bytes))
46    }
47
48    /// Convert the hash to a hex string.
49    pub fn to_hex(&self) -> String {
50        self.0.to_hex().to_string()
51    }
52
53    /// Convert to a hex string limited to the first 5bytes for a friendly string
54    /// representation of the hash.
55    pub fn fmt_short(&self) -> ArrayString<10> {
56        let mut res = ArrayString::new();
57        data_encoding::HEXLOWER
58            .encode_write(&self.as_bytes()[..5], &mut res)
59            .unwrap();
60        res
61    }
62}
63
64impl AsRef<[u8]> for Hash {
65    fn as_ref(&self) -> &[u8] {
66        self.0.as_bytes()
67    }
68}
69
70impl Borrow<[u8]> for Hash {
71    fn borrow(&self) -> &[u8] {
72        self.0.as_bytes()
73    }
74}
75
76impl Borrow<[u8; 32]> for Hash {
77    fn borrow(&self) -> &[u8; 32] {
78        self.0.as_bytes()
79    }
80}
81
82impl From<Hash> for blake3::Hash {
83    fn from(value: Hash) -> Self {
84        value.0
85    }
86}
87
88impl From<blake3::Hash> for Hash {
89    fn from(value: blake3::Hash) -> Self {
90        Hash(value)
91    }
92}
93
94impl From<[u8; 32]> for Hash {
95    fn from(value: [u8; 32]) -> Self {
96        Hash(blake3::Hash::from(value))
97    }
98}
99
100impl From<Hash> for [u8; 32] {
101    fn from(value: Hash) -> Self {
102        *value.as_bytes()
103    }
104}
105
106impl From<&[u8; 32]> for Hash {
107    fn from(value: &[u8; 32]) -> Self {
108        Hash(blake3::Hash::from(*value))
109    }
110}
111
112impl PartialOrd for Hash {
113    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
114        Some(self.0.as_bytes().cmp(other.0.as_bytes()))
115    }
116}
117
118impl Ord for Hash {
119    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
120        self.0.as_bytes().cmp(other.0.as_bytes())
121    }
122}
123
124impl fmt::Display for Hash {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        f.write_str(&self.to_hex())
127        // // result will be 52 bytes
128        // let mut res = [b'b'; 52];
129        // // write the encoded bytes
130        // data_encoding::BASE32_NOPAD.encode_mut(self.as_bytes(), &mut res);
131        // // convert to string, this is guaranteed to succeed
132        // let t = std::str::from_utf8_mut(res.as_mut()).unwrap();
133        // // hack since data_encoding doesn't have BASE32LOWER_NOPAD as a const
134        // t.make_ascii_lowercase();
135        // // write the str, no allocations
136        // f.write_str(t)
137    }
138}
139
140#[common_fields({
141    backtrace: Option<Backtrace>,
142    #[snafu(implicit)]
143    span_trace: SpanTrace,
144})]
145#[allow(missing_docs)]
146#[non_exhaustive]
147#[derive(Debug, Snafu)]
148pub enum HexOrBase32ParseError {
149    #[snafu(display("Invalid length"))]
150    DecodeInvalidLength {},
151    #[snafu(display("Failed to decode {source}"))]
152    Decode { source: data_encoding::DecodeError },
153}
154
155impl FromStr for Hash {
156    type Err = HexOrBase32ParseError;
157
158    fn from_str(s: &str) -> Result<Self, Self::Err> {
159        let mut bytes = [0u8; 32];
160
161        let res = if s.len() == 64 {
162            // hex
163            data_encoding::HEXLOWER.decode_mut(s.as_bytes(), &mut bytes)
164        } else {
165            data_encoding::BASE32_NOPAD.decode_mut(s.to_ascii_uppercase().as_bytes(), &mut bytes)
166        };
167        match res {
168            Ok(len) => {
169                if len != 32 {
170                    return Err(DecodeInvalidLengthSnafu.build());
171                }
172            }
173            Err(partial) => return Err(partial.error).context(DecodeSnafu),
174        }
175        Ok(Self(blake3::Hash::from_bytes(bytes)))
176    }
177}
178
179impl Serialize for Hash {
180    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
181    where
182        S: Serializer,
183    {
184        if serializer.is_human_readable() {
185            serializer.serialize_str(self.to_string().as_str())
186        } else {
187            self.0.as_bytes().serialize(serializer)
188        }
189    }
190}
191
192impl<'de> Deserialize<'de> for Hash {
193    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
194    where
195        D: Deserializer<'de>,
196    {
197        if deserializer.is_human_readable() {
198            let s = String::deserialize(deserializer)?;
199            s.parse().map_err(de::Error::custom)
200        } else {
201            let data: [u8; 32] = Deserialize::deserialize(deserializer)?;
202            Ok(Self(blake3::Hash::from(data)))
203        }
204    }
205}
206
207impl MaxSize for Hash {
208    const POSTCARD_MAX_SIZE: usize = 32;
209}
210
211/// A format identifier
212#[derive(
213    Clone,
214    Copy,
215    PartialEq,
216    Eq,
217    PartialOrd,
218    Ord,
219    Serialize,
220    Deserialize,
221    Default,
222    Debug,
223    MaxSize,
224    Hash,
225    derive_more::Display,
226)]
227pub enum BlobFormat {
228    /// Raw blob
229    #[default]
230    Raw,
231    /// A sequence of BLAKE3 hashes
232    HashSeq,
233}
234
235impl From<BlobFormat> for u64 {
236    fn from(value: BlobFormat) -> Self {
237        match value {
238            BlobFormat::Raw => 0,
239            BlobFormat::HashSeq => 1,
240        }
241    }
242}
243
244impl BlobFormat {
245    /// Is raw format
246    pub const fn is_raw(&self) -> bool {
247        matches!(self, BlobFormat::Raw)
248    }
249
250    /// Is hash seq format
251    pub const fn is_hash_seq(&self) -> bool {
252        matches!(self, BlobFormat::HashSeq)
253    }
254}
255
256/// A hash and format pair
257#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, MaxSize, Hash)]
258pub struct HashAndFormat {
259    /// The hash
260    pub hash: Hash,
261    /// The format
262    pub format: BlobFormat,
263}
264
265impl std::fmt::Debug for HashAndFormat {
266    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267        std::fmt::Debug::fmt(&(DD(self.hash.to_hex()), self.format), f)
268    }
269}
270
271impl From<(Hash, BlobFormat)> for HashAndFormat {
272    fn from((hash, format): (Hash, BlobFormat)) -> Self {
273        Self { hash, format }
274    }
275}
276
277impl From<Hash> for HashAndFormat {
278    fn from(hash: Hash) -> Self {
279        Self {
280            hash,
281            format: BlobFormat::Raw,
282        }
283    }
284}
285
286// #[cfg(feature = "redb")]
287mod redb_support {
288    use postcard::experimental::max_size::MaxSize;
289    use redb::{Key as RedbKey, Value as RedbValue};
290
291    use super::{Hash, HashAndFormat};
292
293    impl RedbValue for Hash {
294        type SelfType<'a> = Self;
295
296        type AsBytes<'a> = &'a [u8; 32];
297
298        fn fixed_width() -> Option<usize> {
299            Some(32)
300        }
301
302        fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
303        where
304            Self: 'a,
305        {
306            let contents: &'a [u8; 32] = data.try_into().unwrap();
307            (*contents).into()
308        }
309
310        fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
311        where
312            Self: 'a,
313            Self: 'b,
314        {
315            value.as_bytes()
316        }
317
318        fn type_name() -> redb::TypeName {
319            redb::TypeName::new("iroh_blobs::Hash")
320        }
321    }
322
323    impl RedbKey for Hash {
324        fn compare(data1: &[u8], data2: &[u8]) -> std::cmp::Ordering {
325            data1.cmp(data2)
326        }
327    }
328
329    impl RedbValue for HashAndFormat {
330        type SelfType<'a> = Self;
331
332        type AsBytes<'a> = [u8; Self::POSTCARD_MAX_SIZE];
333
334        fn fixed_width() -> Option<usize> {
335            Some(Self::POSTCARD_MAX_SIZE)
336        }
337
338        fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
339        where
340            Self: 'a,
341        {
342            let t: &'a [u8; Self::POSTCARD_MAX_SIZE] = data.try_into().unwrap();
343            postcard::from_bytes(t.as_slice()).unwrap()
344        }
345
346        fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
347        where
348            Self: 'a,
349            Self: 'b,
350        {
351            let mut res = [0u8; 33];
352            postcard::to_slice(&value, &mut res).unwrap();
353            res
354        }
355
356        fn type_name() -> redb::TypeName {
357            redb::TypeName::new("iroh_blobs::HashAndFormat")
358        }
359    }
360}
361
362impl HashAndFormat {
363    /// Create a new hash and format pair.
364    pub fn new(hash: Hash, format: BlobFormat) -> Self {
365        Self { hash, format }
366    }
367
368    /// Create a new hash and format pair, using the default (raw) format.
369    pub fn raw(hash: Hash) -> Self {
370        Self {
371            hash,
372            format: BlobFormat::Raw,
373        }
374    }
375
376    /// Create a new hash and format pair, using the collection format.
377    pub fn hash_seq(hash: Hash) -> Self {
378        Self {
379            hash,
380            format: BlobFormat::HashSeq,
381        }
382    }
383}
384
385impl fmt::Display for HashAndFormat {
386    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387        let mut slice = [0u8; 65];
388        hex::encode_to_slice(self.hash.as_bytes(), &mut slice[1..]).unwrap();
389        match self.format {
390            BlobFormat::Raw => {
391                write!(f, "{}", std::str::from_utf8(&slice[1..]).unwrap())
392            }
393            BlobFormat::HashSeq => {
394                slice[0] = b's';
395                write!(f, "{}", std::str::from_utf8(&slice).unwrap())
396            }
397        }
398    }
399}
400
401impl FromStr for HashAndFormat {
402    type Err = anyhow::Error;
403
404    fn from_str(s: &str) -> Result<Self, Self::Err> {
405        let s = s.as_bytes();
406        let mut hash = [0u8; 32];
407        match s.len() {
408            64 => {
409                hex::decode_to_slice(s, &mut hash)?;
410                Ok(Self::raw(hash.into()))
411            }
412            65 if s[0].eq_ignore_ascii_case(&b's') => {
413                hex::decode_to_slice(&s[1..], &mut hash)?;
414                Ok(Self::hash_seq(hash.into()))
415            }
416            _ => anyhow::bail!("invalid hash and format"),
417        }
418    }
419}
420
421impl Serialize for HashAndFormat {
422    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
423    where
424        S: Serializer,
425    {
426        if serializer.is_human_readable() {
427            serializer.serialize_str(self.to_string().as_str())
428        } else {
429            (self.hash, self.format).serialize(serializer)
430        }
431    }
432}
433
434impl<'de> Deserialize<'de> for HashAndFormat {
435    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
436    where
437        D: Deserializer<'de>,
438    {
439        if deserializer.is_human_readable() {
440            let s = String::deserialize(deserializer)?;
441            s.parse().map_err(de::Error::custom)
442        } else {
443            let (hash, format) = <(Hash, BlobFormat)>::deserialize(deserializer)?;
444            Ok(Self { hash, format })
445        }
446    }
447}
448
449#[cfg(test)]
450mod tests {
451
452    use iroh_test::{assert_eq_hex, hexdump::parse_hexdump};
453    use serde_test::{assert_tokens, Configure, Token};
454
455    use super::*;
456
457    #[test]
458    fn test_display_parse_roundtrip() {
459        for i in 0..100 {
460            let hash: Hash = blake3::hash(&[i]).into();
461            let text = hash.to_string();
462            let hash1 = text.parse::<Hash>().unwrap();
463            assert_eq!(hash, hash1);
464
465            let text = hash.to_hex();
466            let hash1 = Hash::from_str(&text).unwrap();
467            assert_eq!(hash, hash1);
468        }
469    }
470
471    #[test]
472    fn test_hash() {
473        let data = b"hello world";
474        let hash = Hash::new(data);
475
476        let encoded = hash.to_string();
477        assert_eq!(encoded.parse::<Hash>().unwrap(), hash);
478    }
479
480    #[test]
481    fn test_empty_hash() {
482        let hash = Hash::new(b"");
483        assert_eq!(hash, Hash::EMPTY);
484    }
485
486    #[test]
487    fn hash_wire_format() {
488        let hash = Hash::from([0xab; 32]);
489        let serialized = postcard::to_stdvec(&hash).unwrap();
490        let expected = parse_hexdump(r"
491            ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab # hash
492        ").unwrap();
493        assert_eq_hex!(serialized, expected);
494    }
495
496    // #[cfg(feature = "redb")]
497    #[test]
498    fn hash_redb() {
499        use redb::Value as RedbValue;
500        let bytes: [u8; 32] = (0..32).collect::<Vec<_>>().as_slice().try_into().unwrap();
501        let hash = Hash::from(bytes);
502        assert_eq!(<Hash as RedbValue>::fixed_width(), Some(32));
503        assert_eq!(
504            <Hash as RedbValue>::type_name(),
505            redb::TypeName::new("iroh_blobs::Hash")
506        );
507        let serialized = <Hash as RedbValue>::as_bytes(&hash);
508        assert_eq!(serialized, &bytes);
509        let deserialized = <Hash as RedbValue>::from_bytes(serialized.as_slice());
510        assert_eq!(deserialized, hash);
511        let expected = parse_hexdump(
512            r"
513            00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
514            10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f # hash
515        ",
516        )
517        .unwrap();
518        assert_eq_hex!(serialized, expected);
519    }
520
521    // #[cfg(feature = "redb")]
522    #[test]
523    fn hash_and_format_redb() {
524        use redb::Value as RedbValue;
525        let hash_bytes: [u8; 32] = (0..32).collect::<Vec<_>>().as_slice().try_into().unwrap();
526        let hash = Hash::from(hash_bytes);
527        let haf = HashAndFormat::raw(hash);
528        assert_eq!(<HashAndFormat as RedbValue>::fixed_width(), Some(33));
529        assert_eq!(
530            <HashAndFormat as RedbValue>::type_name(),
531            redb::TypeName::new("iroh_blobs::HashAndFormat")
532        );
533        let serialized = <HashAndFormat as RedbValue>::as_bytes(&haf);
534        let mut bytes = [0u8; 33];
535        bytes[0..32].copy_from_slice(&hash_bytes);
536        assert_eq!(serialized, bytes);
537        let deserialized = <HashAndFormat as RedbValue>::from_bytes(serialized.as_slice());
538        assert_eq!(deserialized, haf);
539        let expected = parse_hexdump(
540            r"
541            00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
542            10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f # hash
543            00 # format (raw)
544        ",
545        )
546        .unwrap();
547        assert_eq_hex!(serialized, expected);
548    }
549
550    #[test]
551    fn test_hash_serde() {
552        let hash = Hash::new("hello");
553
554        // Hashes are serialized as 32 tuples
555        let mut tokens = Vec::new();
556        tokens.push(Token::Tuple { len: 32 });
557        for byte in hash.as_bytes() {
558            tokens.push(Token::U8(*byte));
559        }
560        tokens.push(Token::TupleEnd);
561        assert_eq!(tokens.len(), 34);
562
563        assert_tokens(&hash.compact(), &tokens);
564
565        let tokens = vec![Token::String(
566            "ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f",
567        )];
568        assert_tokens(&hash.readable(), &tokens);
569    }
570
571    #[test]
572    fn test_hash_postcard() {
573        let hash = Hash::new("hello");
574        let ser = postcard::to_stdvec(&hash).unwrap();
575        let de = postcard::from_bytes(&ser).unwrap();
576        assert_eq!(hash, de);
577
578        assert_eq!(ser.len(), 32);
579    }
580
581    #[test]
582    fn test_hash_json() {
583        let hash = Hash::new("hello");
584        let ser = serde_json::to_string(&hash).unwrap();
585        let de = serde_json::from_str(&ser).unwrap();
586        assert_eq!(hash, de);
587        // 52 bytes of base32 + 2 quotes
588        assert_eq!(ser.len(), 66);
589    }
590
591    #[test]
592    fn test_hash_and_format_parse() {
593        let hash = Hash::new("hello");
594
595        let expected = HashAndFormat::raw(hash);
596        let actual = expected.to_string().parse::<HashAndFormat>().unwrap();
597        assert_eq!(expected, actual);
598
599        let expected = HashAndFormat::hash_seq(hash);
600        let actual = expected.to_string().parse::<HashAndFormat>().unwrap();
601        assert_eq!(expected, actual);
602    }
603
604    #[test]
605    fn test_hash_and_format_postcard() {
606        let haf = HashAndFormat::raw(Hash::new("hello"));
607        let ser = postcard::to_stdvec(&haf).unwrap();
608        let de = postcard::from_bytes(&ser).unwrap();
609        assert_eq!(haf, de);
610    }
611
612    #[test]
613    fn test_hash_and_format_json() {
614        let haf = HashAndFormat::raw(Hash::new("hello"));
615        let ser = serde_json::to_string(&haf).unwrap();
616        let de = serde_json::from_str(&ser).unwrap();
617        assert_eq!(haf, de);
618    }
619}