radix_common/crypto/
hash.rs1use 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#[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
68pub fn hash<T: AsRef<[u8]>>(data: T) -> Hash {
70 blake2b_256_hash(data)
71}
72
73#[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
94impl 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
124impl 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}