cardano_sdk/cardano/
hash.rs

1//  This Source Code Form is subject to the terms of the Mozilla Public
2//  License, v. 2.0. If a copy of the MPL was not distributed with this
3//  file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use crate::{cbor, pallas};
6use anyhow::anyhow;
7use std::{fmt, str::FromStr};
8
9/// A _blake2b_ hash digest; typically 28 or 32 bytes long.
10///
11/// There are several ways to construct [`Self`], but fundamentally:
12///
13/// - Conversions from static byte arrays of known sizes are infaillible:
14///
15///   ```rust
16///   # use cardano_sdk::Hash;
17///   assert_eq!(
18///     <Hash<28>>::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).to_string(),
19///     "00000000000000000000000000000000000000000000000000000000",
20///   );
21///   ```
22///
23/// - Conversions from vectors or slices are possible but faillible:
24///
25///   ```rust
26///   # use cardano_sdk::Hash;
27///   // Vectors contains exactly 28 elements
28///   assert!(
29///     <Hash<28>>::try_from(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
30///         .is_ok()
31///   );
32///
33///   // Vectors still contains only 28 elements
34///   assert!(
35///     <Hash<32>>::try_from(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
36///         .is_err()
37///   );
38///   ```
39///
40/// - Conversions from base16-encoded text strings are also possible:
41///
42///   ```rust
43///   # use cardano_sdk::Hash;
44///   // The text string is indeed 56 character-long.
45///   assert!(
46///     <Hash<28>>::try_from("00000000000000000000000000000000000000000000000000000000")
47///         .is_ok()
48///   );
49///   ```
50///
51/// - For the latter, we also provide the [`hash!`](crate::hash) macro.
52#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, cbor::Encode, cbor::Decode)]
53#[repr(transparent)]
54#[cbor(transparent)]
55pub struct Hash<const SIZE: usize>(#[n(0)] pallas::Hash<SIZE>);
56
57impl<const SIZE: usize> fmt::Display for Hash<SIZE> {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
59        self.0.fmt(f)
60    }
61}
62
63// ---------------------------------------------------------------- Constructing
64
65impl Hash<28> {
66    pub fn new<T: AsRef<[u8]>>(preimage: T) -> Self {
67        Self(pallas::Hasher::<224>::hash(preimage.as_ref()))
68    }
69}
70
71impl Hash<32> {
72    pub fn new<T: AsRef<[u8]>>(preimage: T) -> Self {
73        Self(pallas::Hasher::<256>::hash(preimage.as_ref()))
74    }
75}
76
77// ------------------------------------------------------------------ Inspecting
78
79impl<const SIZE: usize> Hash<SIZE> {
80    pub const SIZE: usize = SIZE;
81}
82
83// ----------------------------------------------------------- Converting (from)
84
85impl<const SIZE: usize> TryFrom<&str> for Hash<SIZE> {
86    type Error = anyhow::Error;
87
88    fn try_from(s: &str) -> anyhow::Result<Self> {
89        let bytes = hex::decode(s).map_err(|e| anyhow!(e))?;
90        let fixed_sized_bytes = <[u8; SIZE]>::try_from(bytes).map_err(|_| {
91            anyhow!(
92                "invalid hex string length; expected {}, got {}",
93                2 * SIZE,
94                s.len()
95            )
96        })?;
97
98        Ok(Hash(pallas::Hash::new(fixed_sized_bytes)))
99    }
100}
101
102impl<const SIZE: usize> FromStr for Hash<SIZE> {
103    type Err = String;
104
105    fn from_str(s: &str) -> Result<Self, Self::Err> {
106        Self::try_from(s).map_err(|e| e.to_string())
107    }
108}
109
110impl<const SIZE: usize> TryFrom<Vec<u8>> for Hash<SIZE> {
111    type Error = anyhow::Error;
112
113    fn try_from(bytes: Vec<u8>) -> anyhow::Result<Self> {
114        let fixed_sized_bytes = <[u8; SIZE]>::try_from(bytes.as_slice()).map_err(|_| {
115            anyhow!(
116                "invalid bytes sequence length; expected {} bytes, got {} bytes",
117                SIZE,
118                bytes.len(),
119            )
120        })?;
121
122        Ok(Hash(pallas::Hash::new(fixed_sized_bytes)))
123    }
124}
125
126impl<const SIZE: usize> From<[u8; SIZE]> for Hash<SIZE> {
127    fn from(hash: [u8; SIZE]) -> Self {
128        Self::from(pallas::Hash::from(hash))
129    }
130}
131
132impl<const SIZE: usize> From<pallas::Hash<SIZE>> for Hash<SIZE> {
133    fn from(hash: pallas::Hash<SIZE>) -> Self {
134        Self(hash)
135    }
136}
137
138impl<const SIZE: usize> From<&pallas::Hash<SIZE>> for Hash<SIZE> {
139    fn from(hash: &pallas::Hash<SIZE>) -> Self {
140        Self(*hash)
141    }
142}
143
144// ------------------------------------------------------------- Converting (to)
145
146impl<const SIZE: usize> From<Hash<SIZE>> for pallas::Hash<SIZE> {
147    fn from(hash: Hash<SIZE>) -> Self {
148        hash.0
149    }
150}
151
152impl<const SIZE: usize> From<&Hash<SIZE>> for pallas::Hash<SIZE> {
153    fn from(hash: &Hash<SIZE>) -> Self {
154        hash.0
155    }
156}
157
158impl<const SIZE: usize> From<Hash<SIZE>> for [u8; SIZE] {
159    fn from(hash: Hash<SIZE>) -> Self {
160        *hash.0
161    }
162}
163
164impl<const SIZE: usize> AsRef<[u8]> for Hash<SIZE> {
165    fn as_ref(&self) -> &[u8] {
166        self.0.as_ref()
167    }
168}
169
170#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, cbor::Encode, cbor::Decode)]
171#[repr(transparent)]
172#[cbor(transparent)]
173pub struct Hash28(#[n(0)] Hash<28>);
174
175impl From<Hash<28>> for Hash28 {
176    fn from(hash28: Hash<28>) -> Self {
177        Self(hash28)
178    }
179}
180
181impl std::ops::Deref for Hash28 {
182    type Target = Hash<28>;
183    fn deref(&self) -> &Self::Target {
184        &self.0
185    }
186}
187
188#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, cbor::Encode, cbor::Decode)]
189#[repr(transparent)]
190#[cbor(transparent)]
191pub struct Hash32(#[n(0)] Hash<32>);
192
193impl From<Hash<32>> for Hash32 {
194    fn from(hash32: Hash<32>) -> Self {
195        Self(hash32)
196    }
197}
198
199impl std::ops::Deref for Hash32 {
200    type Target = Hash<32>;
201    fn deref(&self) -> &Self::Target {
202        &self.0
203    }
204}
205
206#[cfg(any(test, feature = "test-utils"))]
207pub mod tests {
208    use crate::Hash;
209    use proptest::prelude::*;
210
211    // -------------------------------------------------------------- Generators
212
213    pub mod generators {
214        use super::*;
215
216        pub fn hash28() -> impl Strategy<Value = Hash<28>> {
217            any::<[u8; 28]>().prop_map(Hash::from)
218        }
219
220        pub fn hash32() -> impl Strategy<Value = Hash<32>> {
221            any::<[u8; 32]>().prop_map(Hash::from)
222        }
223    }
224}