pallas_crypto/hash/
hash.rs

1use pallas_codec::minicbor;
2use std::{fmt, ops::Deref, str::FromStr};
3
4/// data that is a cryptographic [`struct@Hash`] of `BYTES` long.
5///
6/// Possible values with Cardano are 32 bytes long (block hash or transaction
7/// hash). Or 28 bytes long (as used in addresses)
8#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct Hash<const BYTES: usize>([u8; BYTES]);
10
11impl<const BYTES: usize> Hash<BYTES> {
12    #[inline]
13    pub const fn new(bytes: [u8; BYTES]) -> Self {
14        Self(bytes)
15    }
16}
17
18impl<const BYTES: usize> From<[u8; BYTES]> for Hash<BYTES> {
19    #[inline]
20    fn from(bytes: [u8; BYTES]) -> Self {
21        Self::new(bytes)
22    }
23}
24
25impl<const BYTES: usize> From<&[u8]> for Hash<BYTES> {
26    fn from(value: &[u8]) -> Self {
27        let mut hash = [0; BYTES];
28        hash.copy_from_slice(value);
29        Self::new(hash)
30    }
31}
32
33impl<const BYTES: usize> AsRef<[u8]> for Hash<BYTES> {
34    #[inline]
35    fn as_ref(&self) -> &[u8] {
36        &self.0
37    }
38}
39
40impl<const BYTES: usize> Deref for Hash<BYTES> {
41    type Target = [u8; BYTES];
42
43    #[inline]
44    fn deref(&self) -> &Self::Target {
45        &self.0
46    }
47}
48
49impl<const BYTES: usize> PartialEq<[u8]> for Hash<BYTES> {
50    fn eq(&self, other: &[u8]) -> bool {
51        self.0.eq(other)
52    }
53}
54
55impl<const BYTES: usize> fmt::Debug for Hash<BYTES> {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.debug_tuple(&format!("Hash<{BYTES}>"))
58            .field(&hex::encode(self))
59            .finish()
60    }
61}
62
63impl<const BYTES: usize> fmt::Display for Hash<BYTES> {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        f.write_str(&hex::encode(self))
66    }
67}
68
69impl<const BYTES: usize> FromStr for Hash<BYTES> {
70    type Err = hex::FromHexError;
71    fn from_str(s: &str) -> Result<Self, Self::Err> {
72        let mut bytes = [0; BYTES];
73        hex::decode_to_slice(s, &mut bytes)?;
74        Ok(Self::new(bytes))
75    }
76}
77
78impl<C, const BYTES: usize> minicbor::Encode<C> for Hash<BYTES> {
79    fn encode<W: minicbor::encode::Write>(
80        &self,
81        e: &mut minicbor::Encoder<W>,
82        _ctx: &mut C,
83    ) -> Result<(), minicbor::encode::Error<W::Error>> {
84        e.bytes(&self.0)?.ok()
85    }
86}
87
88impl<'a, C, const BYTES: usize> minicbor::Decode<'a, C> for Hash<BYTES> {
89    fn decode(
90        d: &mut minicbor::Decoder<'a>,
91        _ctx: &mut C,
92    ) -> Result<Self, minicbor::decode::Error> {
93        let bytes = d.bytes()?;
94        if bytes.len() == BYTES {
95            let mut hash = [0; BYTES];
96            hash.copy_from_slice(bytes);
97            Ok(Self::new(hash))
98        } else {
99            // TODO: minicbor does not allow for expecting a specific size byte array
100            //       (in fact cbor is not good at it at all anyway)
101            Err(minicbor::decode::Error::message("Invalid hash size"))
102        }
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn from_str() {
112        let _digest: Hash<28> = "276fd18711931e2c0e21430192dbeac0e458093cd9d1fcd7210f64b3"
113            .parse()
114            .unwrap();
115
116        let _digest: Hash<32> = "0d8d00cdd4657ac84d82f0a56067634a7adfdf43da41cb534bcaa45060973d21"
117            .parse()
118            .unwrap();
119    }
120
121    #[test]
122    #[should_panic]
123    fn from_str_fail_1() {
124        let _digest: Hash<28> = "27".parse().unwrap();
125    }
126
127    #[test]
128    #[should_panic]
129    fn from_str_fail_2() {
130        let _digest: Hash<32> = "0d8d00cdd465".parse().unwrap();
131    }
132}