radix_common/crypto/
hash.rs

1use crate::crypto::blake2b_256_hash;
2use radix_rust::copy_u8_array;
3use sbor::rust::borrow::ToOwned;
4use sbor::rust::convert::TryFrom;
5use sbor::rust::fmt;
6use sbor::rust::str::FromStr;
7use sbor::rust::string::String;
8use sbor::rust::vec::Vec;
9use sbor::*;
10
11/// Represents a 32-byte hash digest.
12#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Sbor)]
13#[sbor(transparent)]
14pub struct Hash(pub [u8; Self::LENGTH]);
15
16impl Hash {
17    pub const LENGTH: usize = 32;
18
19    pub fn lower_bytes<const N: usize>(&self) -> [u8; N] {
20        self.0[(Self::LENGTH - N)..Self::LENGTH].try_into().unwrap()
21    }
22}
23
24pub trait IsHash: AsRef<[u8]> + Sized + From<Hash> + Into<Hash> + AsRef<Hash> {
25    fn as_bytes(&self) -> &[u8; Hash::LENGTH] {
26        &<Self as AsRef<Hash>>::as_ref(self).0
27    }
28
29    fn as_slice(&self) -> &[u8] {
30        &<Self as AsRef<Hash>>::as_ref(self).0
31    }
32
33    fn as_hash(&self) -> &Hash {
34        <Self as AsRef<Hash>>::as_ref(self)
35    }
36
37    fn into_bytes(self) -> [u8; Hash::LENGTH] {
38        self.into_hash().0
39    }
40
41    fn into_hash(self) -> Hash {
42        self.into()
43    }
44
45    fn from_bytes(bytes: [u8; Hash::LENGTH]) -> Self {
46        Hash(bytes).into()
47    }
48
49    fn from_hash(hash: Hash) -> Self {
50        hash.into()
51    }
52}
53
54impl IsHash for Hash {}
55
56impl AsRef<Hash> for Hash {
57    fn as_ref(&self) -> &Hash {
58        self
59    }
60}
61
62impl AsRef<[u8]> for Hash {
63    fn as_ref(&self) -> &[u8] {
64        &self.0
65    }
66}
67
68/// Computes the hash digest of a message.
69pub fn hash<T: AsRef<[u8]>>(data: T) -> Hash {
70    blake2b_256_hash(data)
71}
72
73//========
74// error
75//========
76
77/// Represents an error when parsing hash.
78#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
79pub enum ParseHashError {
80    InvalidHex(String),
81    InvalidLength { actual: usize, expected: usize },
82}
83
84#[cfg(not(feature = "alloc"))]
85impl std::error::Error for ParseHashError {}
86
87#[cfg(not(feature = "alloc"))]
88impl fmt::Display for ParseHashError {
89    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90        write!(f, "{:?}", self)
91    }
92}
93
94//========
95// binary
96//========
97
98impl TryFrom<&[u8]> for Hash {
99    type Error = ParseHashError;
100
101    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
102        if slice.len() != Hash::LENGTH {
103            return Err(ParseHashError::InvalidLength {
104                actual: slice.len(),
105                expected: Hash::LENGTH,
106            });
107        }
108        Ok(Self(copy_u8_array(slice)))
109    }
110}
111
112impl From<Hash> for Vec<u8> {
113    fn from(value: Hash) -> Self {
114        value.to_vec()
115    }
116}
117
118impl Hash {
119    pub fn to_vec(&self) -> Vec<u8> {
120        self.0.to_vec()
121    }
122}
123
124//======
125// text
126//======
127
128impl FromStr for Hash {
129    type Err = ParseHashError;
130
131    fn from_str(s: &str) -> Result<Self, Self::Err> {
132        let bytes = hex::decode(s).map_err(|_| ParseHashError::InvalidHex(s.to_owned()))?;
133        Self::try_from(bytes.as_slice())
134    }
135}
136
137impl fmt::Display for Hash {
138    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
139        write!(f, "{}", hex::encode(self.0))
140    }
141}
142
143impl fmt::Debug for Hash {
144    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
145        write!(f, "{}", self)
146    }
147}
148
149#[macro_export]
150macro_rules! define_wrapped_hash {
151    ($(#[$docs:meta])* $name:ident) => {
152        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Sbor)]
153        #[sbor(transparent)]
154        $(#[$docs])*
155        pub struct $name(pub Hash);
156
157        impl AsRef<[u8]> for $name {
158            fn as_ref(&self) -> &[u8] {
159                self.0.as_ref()
160            }
161        }
162
163        impl AsRef<Hash> for $name {
164            fn as_ref(&self) -> &Hash {
165                &self.0
166            }
167        }
168
169        impl From<Hash> for $name {
170            fn from(value: Hash) -> Self {
171                Self(value)
172            }
173        }
174
175        impl From<$name> for Hash {
176            fn from(value: $name) -> Self {
177                value.0
178            }
179        }
180
181        impl IsHash for $name {}
182    };
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use sbor::rust::string::ToString;
189
190    #[test]
191    fn test_from_to_string() {
192        let s = "b177968c9c68877dc8d33e25759183c556379daa45a4d78a2b91c70133c873ca";
193        let h = Hash::from_str(s).unwrap();
194        assert_eq!(h.to_string(), s);
195    }
196}