1use std::convert::TryFrom;
5use std::hash::{Hash, Hasher};
6use std::str::FromStr;
7use std::{error, fmt, result};
8
9use cryptoxide::blake2b::Blake2b;
10use cryptoxide::digest::Digest as _;
11use cryptoxide::sha3;
12use hex::FromHexError;
13
14use crate::typed_bytes::ByteSlice;
15
16use crate::chain_crypto::bech32::{self, Bech32};
17use crate::chain_crypto::hash::{Blake2b256, Sha3_256};
18
19#[derive(Debug, PartialEq, Clone)]
20pub enum Error {
21 InvalidDigestSize { got: usize, expected: usize },
22 InvalidHexEncoding(FromHexError),
23}
24impl fmt::Display for Error {
25 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26 match self {
27 Error::InvalidDigestSize { got: sz, expected } => write!(
28 f,
29 "invalid digest size, expected {expected} but received {sz} bytes."
30 ),
31 Error::InvalidHexEncoding(_) => write!(f, "invalid hex encoding for digest value"),
32 }
33 }
34}
35
36impl error::Error for Error {
37 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
38 match self {
39 Error::InvalidDigestSize {
40 got: _,
41 expected: _,
42 } => None,
43 Error::InvalidHexEncoding(err) => Some(err),
44 }
45 }
46}
47
48impl From<FromHexError> for Error {
49 fn from(err: FromHexError) -> Self {
50 Error::InvalidHexEncoding(err)
51 }
52}
53
54#[derive(Debug, Copy, Clone)]
55pub struct TryFromSliceError(());
56
57impl fmt::Display for TryFromSliceError {
58 #[inline]
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 fmt::Display::fmt("could not convert slice to digest", f)
61 }
62}
63
64pub trait DigestAlg {
65 const HASH_SIZE: usize;
66 type DigestData: Clone + PartialEq + Hash + Send + AsRef<[u8]>;
67 type DigestContext: Clone;
68
69 fn try_from_slice(slice: &[u8]) -> Result<Self::DigestData, Error>;
70 fn new() -> Self::DigestContext;
71 fn append_data(ctx: &mut Self::DigestContext, data: &[u8]);
72 fn finalize(ctx: Self::DigestContext) -> Self::DigestData;
73}
74
75impl DigestAlg for Blake2b256 {
76 const HASH_SIZE: usize = 32;
77 type DigestData = [u8; Self::HASH_SIZE];
78 type DigestContext = Blake2b;
79
80 fn try_from_slice(slice: &[u8]) -> Result<Self::DigestData, Error> {
81 if slice.len() == Self::HASH_SIZE {
82 let mut out = [0u8; Self::HASH_SIZE];
83 out.copy_from_slice(slice);
84 Ok(out)
85 } else {
86 Err(Error::InvalidDigestSize {
87 expected: Self::HASH_SIZE,
88 got: slice.len(),
89 })
90 }
91 }
92
93 fn new() -> Self::DigestContext {
94 Blake2b::new(Self::HASH_SIZE)
95 }
96
97 fn append_data(ctx: &mut Self::DigestContext, data: &[u8]) {
98 ctx.input(data)
99 }
100
101 fn finalize(mut ctx: Self::DigestContext) -> Self::DigestData {
102 let mut out: Self::DigestData = [0; Self::HASH_SIZE];
103 ctx.result(&mut out);
104 out
105 }
106}
107
108impl DigestAlg for Sha3_256 {
109 const HASH_SIZE: usize = 32;
110 type DigestData = [u8; Self::HASH_SIZE];
111 type DigestContext = sha3::Sha3_256;
112
113 fn try_from_slice(slice: &[u8]) -> Result<Self::DigestData, Error> {
114 if slice.len() == Self::HASH_SIZE {
115 let mut out = [0u8; Self::HASH_SIZE];
116 out.copy_from_slice(slice);
117 Ok(out)
118 } else {
119 Err(Error::InvalidDigestSize {
120 expected: Self::HASH_SIZE,
121 got: slice.len(),
122 })
123 }
124 }
125
126 fn new() -> Self::DigestContext {
127 sha3::Sha3_256::new()
128 }
129
130 fn append_data(ctx: &mut Self::DigestContext, data: &[u8]) {
131 ctx.input(data)
132 }
133
134 fn finalize(mut ctx: Self::DigestContext) -> Self::DigestData {
135 let mut out: Self::DigestData = [0; Self::HASH_SIZE];
136 ctx.result(&mut out);
137 out
138 }
139}
140
141pub struct Context<H: DigestAlg>(H::DigestContext);
143
144impl<H: DigestAlg> Clone for Context<H> {
145 fn clone(&self) -> Self {
146 Self(self.0.clone())
147 }
148}
149
150impl<H: DigestAlg> Context<H> {
151 pub fn new() -> Self {
153 Self(H::new())
154 }
155
156 pub fn append_data(&mut self, data: &[u8]) {
158 H::append_data(&mut self.0, data)
159 }
160
161 pub fn finalize(self) -> Digest<H> {
163 Digest(H::finalize(self.0))
164 }
165}
166
167impl<H: DigestAlg> Default for Context<H> {
168 fn default() -> Self {
169 Self::new()
170 }
171}
172
173pub struct Digest<H: DigestAlg>(H::DigestData);
174
175impl<H: DigestAlg> Clone for Digest<H> {
176 fn clone(&self) -> Self {
177 Self(self.0.clone())
178 }
179}
180
181macro_rules! define_from_instances {
182 ($hash_ty:ty, $hash_size:expr, $bech32_hrp:expr) => {
183 impl From<Digest<$hash_ty>> for [u8; $hash_size] {
184 fn from(digest: Digest<$hash_ty>) -> Self {
185 digest.0
186 }
187 }
188
189 impl<'a> From<&'a Digest<$hash_ty>> for &'a [u8; $hash_size] {
190 fn from(digest: &'a Digest<$hash_ty>) -> Self {
191 &digest.0
192 }
193 }
194
195 impl From<[u8; $hash_size]> for Digest<$hash_ty> {
196 fn from(bytes: [u8; $hash_size]) -> Self {
197 Digest(bytes)
198 }
199 }
200 impl From<$hash_ty> for Digest<$hash_ty> {
201 fn from(bytes: $hash_ty) -> Self {
202 let out: [u8; $hash_size] = bytes.into();
203 out.into()
204 }
205 }
206 impl Bech32 for Digest<$hash_ty> {
207 const BECH32_HRP: &'static str = $bech32_hrp;
208
209 fn try_from_bech32_str(bech32_str: &str) -> bech32::Result<Self> {
210 let bytes = bech32::try_from_bech32_to_bytes::<Self>(bech32_str)?;
211 Digest::try_from(&bytes[..]).map_err(bech32::Error::data_invalid)
212 }
213
214 fn to_bech32_str(&self) -> String {
215 bech32::to_bech32_from_bytes::<Self>(self.as_ref())
216 }
217 }
218 };
219}
220
221define_from_instances!(Sha3_256, 32, "sha3");
222define_from_instances!(Blake2b256, 32, "blake2b");
223
224unsafe impl<H: DigestAlg> Send for Digest<H> {}
225
226impl<H: DigestAlg> PartialEq for Digest<H> {
227 fn eq(&self, other: &Self) -> bool {
228 self.0 == other.0
229 }
230}
231
232impl<H: DigestAlg> Eq for Digest<H> {}
233
234impl<H: DigestAlg> PartialOrd for Digest<H> {
235 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
236 Some(self.cmp(other))
237 }
238}
239
240impl<H: DigestAlg> Ord for Digest<H> {
241 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
242 self.as_ref().cmp(other.as_ref())
243 }
244}
245
246impl<H: DigestAlg> AsRef<[u8]> for Digest<H> {
247 fn as_ref(&self) -> &[u8] {
248 self.0.as_ref()
249 }
250}
251
252impl<H: DigestAlg> Hash for Digest<H> {
253 fn hash<HA: Hasher>(&self, state: &mut HA) {
254 self.0.hash(state)
255 }
256}
257
258impl<H: DigestAlg> TryFrom<&[u8]> for Digest<H> {
259 type Error = Error;
260 fn try_from(slice: &[u8]) -> Result<Digest<H>, Self::Error> {
261 <H as DigestAlg>::try_from_slice(slice).map(Digest)
262 }
263}
264
265impl<H: DigestAlg> FromStr for Digest<H> {
266 type Err = Error;
267 fn from_str(s: &str) -> result::Result<Digest<H>, Self::Err> {
268 let bytes = hex::decode(s)?;
269 Digest::try_from(&bytes[..])
270 }
271}
272
273impl<H: DigestAlg> fmt::Display for Digest<H> {
274 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
275 write!(f, "{}", hex::encode(self.as_ref()))
276 }
277}
278impl<H: DigestAlg> fmt::Debug for Digest<H> {
279 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
280 f.write_str(concat!(stringify!($hash_ty), "(0x"))?;
281 write!(f, "{}", hex::encode(self.as_ref()))?;
282 f.write_str(")")
283 }
284}
285
286impl<H: DigestAlg> Digest<H> {
287 #[allow(clippy::self_named_constructors)]
289 pub fn digest(slice: &[u8]) -> Self {
290 let mut ctx = Context::new();
291 ctx.append_data(slice);
292 ctx.finalize()
293 }
294}
295
296use std::marker::PhantomData;
297
298pub struct DigestOf<H: DigestAlg, T> {
300 inner: Digest<H>,
301 marker: PhantomData<T>,
302}
303
304unsafe impl<H: DigestAlg, T> Send for DigestOf<H, T> {}
305
306impl<H: DigestAlg, T> Clone for DigestOf<H, T> {
307 fn clone(&self) -> Self {
308 DigestOf {
309 inner: self.inner.clone(),
310 marker: self.marker,
311 }
312 }
313}
314
315impl<H: DigestAlg, T> From<DigestOf<H, T>> for Digest<H> {
316 fn from(d: DigestOf<H, T>) -> Self {
317 d.inner
318 }
319}
320
321impl<H: DigestAlg, T> From<Digest<H>> for DigestOf<H, T> {
322 fn from(d: Digest<H>) -> Self {
323 DigestOf {
324 inner: d,
325 marker: PhantomData,
326 }
327 }
328}
329
330impl<H: DigestAlg, T> PartialEq for DigestOf<H, T> {
331 fn eq(&self, other: &Self) -> bool {
332 self.inner == other.inner
333 }
334}
335
336impl<H: DigestAlg, T> Eq for DigestOf<H, T> {}
337
338impl<H: DigestAlg, T> PartialOrd for DigestOf<H, T> {
339 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
340 Some(self.cmp(other))
341 }
342}
343
344impl<H: DigestAlg, T> Ord for DigestOf<H, T> {
345 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
346 self.inner.cmp(&other.inner)
347 }
348}
349
350impl<H: DigestAlg, T> AsRef<[u8]> for DigestOf<H, T> {
351 fn as_ref(&self) -> &[u8] {
352 self.inner.as_ref()
353 }
354}
355
356impl<H: DigestAlg, T> Hash for DigestOf<H, T> {
357 fn hash<HA: Hasher>(&self, state: &mut HA) {
358 self.inner.hash(state)
359 }
360}
361
362impl<H: DigestAlg, T> TryFrom<&[u8]> for DigestOf<H, T> {
363 type Error = Error;
364 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
365 Digest::<H>::try_from(slice).map(|d| d.into())
366 }
367}
368
369impl<H: DigestAlg, T> FromStr for DigestOf<H, T> {
370 type Err = Error;
371 fn from_str(s: &str) -> result::Result<Self, Self::Err> {
372 Digest::<H>::from_str(s).map(|d| d.into())
373 }
374}
375
376impl<H: DigestAlg, T> fmt::Display for DigestOf<H, T> {
377 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
378 self.inner.fmt(f)
379 }
380}
381impl<H: DigestAlg, T> fmt::Debug for DigestOf<H, T> {
382 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
383 self.inner.fmt(f)
384 }
385}
386
387impl<H: DigestAlg, T> DigestOf<H, T> {
388 pub fn coerce<U>(&self) -> DigestOf<H, U> {
390 DigestOf {
391 inner: self.inner.clone(),
392 marker: PhantomData,
393 }
394 }
395
396 pub fn digest_byteslice(byteslice: &ByteSlice<T>) -> Self {
397 let mut ctx = Context::new();
398 ctx.append_data(byteslice.as_slice());
399 DigestOf {
400 inner: ctx.finalize(),
401 marker: PhantomData,
402 }
403 }
404
405 pub fn digest(obj: &T) -> Self
407 where
408 T: AsRef<[u8]>,
409 {
410 let mut ctx = Context::new();
411 ctx.append_data(obj.as_ref());
412 DigestOf {
413 inner: ctx.finalize(),
414 marker: PhantomData,
415 }
416 }
417
418 pub fn digest_with<F>(obj: &T, f: F) -> Self
420 where
421 F: FnOnce(&T) -> &[u8],
422 {
423 let mut ctx = Context::new();
424 ctx.append_data(f(obj));
425 DigestOf {
426 inner: ctx.finalize(),
427 marker: PhantomData,
428 }
429 }
430}
431
432macro_rules! typed_define_from_instances {
433 ($hash_ty:ty, $hash_size:expr, $bech32_hrp:expr) => {
434 impl<T> From<DigestOf<$hash_ty, T>> for [u8; $hash_size] {
435 fn from(digest: DigestOf<$hash_ty, T>) -> Self {
436 digest.inner.into()
437 }
438 }
439 impl<'a, T> From<&'a DigestOf<$hash_ty, T>> for &'a [u8; $hash_size] {
440 fn from(digest: &'a DigestOf<$hash_ty, T>) -> Self {
441 (&digest.inner).into()
442 }
443 }
444
445 impl<T> From<[u8; $hash_size]> for DigestOf<$hash_ty, T> {
446 fn from(bytes: [u8; $hash_size]) -> Self {
447 Digest::from(bytes).into()
448 }
449 }
450
451 impl<T> From<$hash_ty> for DigestOf<$hash_ty, T> {
452 fn from(bytes: $hash_ty) -> Self {
453 let out: [u8; $hash_size] = bytes.into();
454 out.into()
455 }
456 }
457 impl<T> Bech32 for DigestOf<$hash_ty, T> {
458 const BECH32_HRP: &'static str = $bech32_hrp;
459
460 fn try_from_bech32_str(bech32_str: &str) -> bech32::Result<Self> {
461 let bytes = bech32::try_from_bech32_to_bytes::<Self>(bech32_str)?;
462 Digest::try_from(&bytes[..])
463 .map_err(bech32::Error::data_invalid)
464 .map(|d| d.into())
465 }
466
467 fn to_bech32_str(&self) -> String {
468 bech32::to_bech32_from_bytes::<Self>(self.inner.as_ref())
469 }
470 }
471 };
472}
473
474typed_define_from_instances!(Sha3_256, 32, "sha3");
475typed_define_from_instances!(Blake2b256, 32, "blake2b");