concordium_contracts_common/
hashes.rs

1//! Different types of hashes based on SHA256.
2
3use crate::constants::SHA256;
4#[cfg(all(feature = "derive-serde", not(feature = "std")))]
5use core::str::FromStr;
6#[cfg(not(feature = "std"))]
7use core::{
8    convert::{TryFrom, TryInto},
9    fmt, hash,
10    marker::PhantomData,
11    ops::Deref,
12};
13#[cfg(feature = "derive-serde")]
14use serde;
15#[cfg(all(feature = "derive-serde", feature = "std"))]
16use std::str::FromStr;
17#[cfg(feature = "std")]
18use std::{
19    convert::{TryFrom, TryInto},
20    fmt, hash,
21    marker::PhantomData,
22    ops::Deref,
23};
24
25#[derive(Ord, PartialOrd, Copy)]
26#[cfg_attr(feature = "derive-serde", derive(serde::Serialize))]
27#[cfg_attr(feature = "derive-serde", serde(into = "String"))]
28#[repr(transparent)]
29/// A general wrapper around Sha256 hash. This is used to add type safety to
30/// a hash that is used in different context. The underlying value is always the
31/// same, but the phantom type variable makes it impossible to mistakenly misuse
32/// the hashes.
33pub struct HashBytes<Purpose> {
34    pub bytes: [u8; SHA256],
35    #[cfg_attr(feature = "derive-serde", serde(skip))] // use default when deserializing
36    _phantom: PhantomData<Purpose>,
37}
38
39impl<Purpose> PartialEq for HashBytes<Purpose> {
40    fn eq(&self, other: &Self) -> bool { self.bytes == other.bytes }
41}
42
43impl<Purpose> Eq for HashBytes<Purpose> {}
44
45impl<Purpose> hash::Hash for HashBytes<Purpose> {
46    fn hash<H: hash::Hasher>(&self, state: &mut H) { self.bytes.hash(state); }
47}
48
49impl<Purpose> Clone for HashBytes<Purpose> {
50    fn clone(&self) -> Self {
51        Self {
52            bytes:    self.bytes,
53            _phantom: Default::default(),
54        }
55    }
56}
57
58impl<Purpose> crate::Serial for HashBytes<Purpose> {
59    fn serial<W: crate::Write>(&self, out: &mut W) -> Result<(), W::Err> {
60        out.write_all(self.as_ref())
61    }
62}
63
64impl<Purpose> crate::Deserial for HashBytes<Purpose> {
65    fn deserial<R: crate::Read>(source: &mut R) -> crate::ParseResult<Self> {
66        let bytes: [u8; 32] = <[u8; 32]>::deserial(source)?;
67        Ok(bytes.into())
68    }
69}
70
71#[doc(hidden)]
72#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
73/// Used as a phantom type to indicate a hash is a block hash.
74pub enum ModuleReferenceMarker {}
75
76/// A reference to a smart contract module deployed on the chain.
77pub type ModuleReference = HashBytes<ModuleReferenceMarker>;
78
79#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
80/// Used as a phantom type to indicate a hash is a hash with no specific
81/// meaning.
82pub enum PureHashMarker {}
83
84/// A Sha256 with no specific meaning.
85pub type Hash = HashBytes<PureHashMarker>;
86
87#[cfg(feature = "derive-serde")]
88#[derive(Debug, thiserror::Error)]
89/// Possible errors when converting from a string to any kind of hash.
90/// String representation of hashes is as base 16.
91pub enum HashFromStrError {
92    #[cfg_attr(feature = "derive-serde", error("Not a valid hex string: {0}"))]
93    HexDecodeError(#[from] hex::FromHexError),
94    #[cfg_attr(feature = "derive-serde", error("Incorrect length, found {found}, expected 32."))]
95    IncorrectLength {
96        // length that was found
97        found: usize,
98    },
99}
100
101impl<Purpose> HashBytes<Purpose> {
102    /// Construct [`HashBytes`] from a slice.
103    pub const fn new(bytes: [u8; SHA256]) -> Self {
104        Self {
105            bytes,
106            _phantom: PhantomData,
107        }
108    }
109}
110
111impl<Purpose> From<[u8; 32]> for HashBytes<Purpose> {
112    fn from(array: [u8; 32]) -> Self { Self::new(array) }
113}
114
115impl<Purpose> Deref for HashBytes<Purpose> {
116    type Target = [u8];
117
118    fn deref(&self) -> &Self::Target { &self.bytes }
119}
120
121impl<Purpose> AsRef<[u8]> for HashBytes<Purpose> {
122    fn as_ref(&self) -> &[u8] { self }
123}
124
125#[cfg(feature = "derive-serde")]
126impl<Purpose> FromStr for HashBytes<Purpose> {
127    type Err = HashFromStrError;
128
129    fn from_str(s: &str) -> Result<Self, Self::Err> {
130        let hex_decoded = hex::decode(s)?;
131        let found = hex_decoded.len();
132        let bytes = hex_decoded.try_into().map_err(|_| HashFromStrError::IncorrectLength {
133            found,
134        })?;
135        Ok(HashBytes::new(bytes))
136    }
137}
138
139#[cfg(feature = "derive-serde")]
140impl<Purpose> TryFrom<&str> for HashBytes<Purpose> {
141    type Error = HashFromStrError;
142
143    fn try_from(value: &str) -> Result<Self, Self::Error> { Self::from_str(value) }
144}
145
146#[derive(Debug)]
147#[cfg_attr(feature = "derive-serde", derive(thiserror::Error))]
148#[cfg_attr(feature = "derive-serde", error("Slice has incompatible length with a hash."))]
149pub struct IncorrectLength;
150
151impl<Purpose> TryFrom<&[u8]> for HashBytes<Purpose> {
152    type Error = IncorrectLength;
153
154    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
155        let bytes: [u8; SHA256] = value.try_into().map_err(|_| IncorrectLength)?;
156        Ok(bytes.into())
157    }
158}
159
160// NB: Using try_from = &str does not work correctly for HashBytes
161// serde::Deserialize instance due to ownership issues for the JSON format.
162// Hence we implement this manually. The issue is that a &'a str cannot be
163// deserialized from a String for arbitrary 'a, and this is needed when
164// parsing with serde_json::from_value via &'a str.
165#[cfg(feature = "derive-serde")]
166impl<'de, Purpose> serde::Deserialize<'de> for HashBytes<Purpose> {
167    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
168    where
169        D: serde::Deserializer<'de>, {
170        struct HashBytesVisitor<Purpose> {
171            _phantom: PhantomData<Purpose>,
172        }
173
174        impl<'de, Purpose> serde::de::Visitor<'de> for HashBytesVisitor<Purpose> {
175            type Value = HashBytes<Purpose>;
176
177            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
178                write!(formatter, "A hex string.")
179            }
180
181            fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
182                HashBytes::try_from(v).map_err(E::custom)
183            }
184        }
185
186        deserializer.deserialize_str(HashBytesVisitor {
187            _phantom: Default::default(),
188        })
189    }
190}
191
192#[cfg(feature = "derive-serde")]
193impl<Purpose> From<HashBytes<Purpose>> for String {
194    fn from(x: HashBytes<Purpose>) -> String { x.to_string() }
195}
196
197// a short, 8-character beginning of the SHA
198impl<Purpose> fmt::Debug for HashBytes<Purpose> {
199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200        for byte in self.iter().take(4) {
201            write!(f, "{:02x}", byte)?;
202        }
203        Ok(())
204    }
205}
206
207// the full SHA256 in hex
208impl<Purpose> fmt::Display for HashBytes<Purpose> {
209    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210        for byte in self.iter() {
211            write!(f, "{:02x}", byte)?;
212        }
213        Ok(())
214    }
215}