1use anyhow::Error;
2use once_cell::sync::Lazy;
3use serde::{Deserialize, Serialize};
4use std::{fmt, str::FromStr};
5use thiserror::Error;
6
7mod dynamic;
8mod r#static;
9
10pub use digest::{Digest, Output};
11pub use dynamic::{AnyHash, AnyHashError};
12pub use r#static::Hash;
13pub use sha2::Sha256;
14
15use crate::VisitBytes;
16
17use self::r#static::IncorrectLengthError;
18
19#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
20#[serde(rename_all = "camelCase")]
21#[non_exhaustive]
22pub enum HashAlgorithm {
23 Sha256,
24}
25
26impl fmt::Display for HashAlgorithm {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 match self {
29 HashAlgorithm::Sha256 => write!(f, "sha256"),
30 }
31 }
32}
33
34impl FromStr for HashAlgorithm {
35 type Err = Error;
36
37 fn from_str(s: &str) -> Result<Self, Self::Err> {
38 match s {
39 "sha256" => Ok(HashAlgorithm::Sha256),
40 _ => Err(Error::msg(format!("Illegal hash algorithm '{}'", s))),
41 }
42 }
43}
44
45static EMPTY_TREE_HASH: Lazy<Vec<Hash<Sha256>>> = Lazy::new(|| {
46 let mut v: Vec<Hash<Sha256>> = Vec::with_capacity(257);
47 fn empty_tree_hash<D: SupportedDigest>(v: &mut Vec<Hash<D>>, height: u32) -> Hash<D> {
48 let hash: Hash<D> = if height == 0 {
49 hash_empty()
50 } else {
51 let last_hash = empty_tree_hash(v, height - 1);
52 hash_branch(&last_hash, &last_hash)
53 };
54 v.push(hash.clone());
55 hash
56 }
57 empty_tree_hash(&mut v, 256);
58 v
59});
60
61pub(crate) fn hash_empty<D: SupportedDigest>() -> Hash<D> {
63 hash_leaf(())
64}
65
66pub(crate) fn hash_leaf<D, V>(value: V) -> Hash<D>
68where
69 D: SupportedDigest,
70 V: VisitBytes,
71{
72 Hash::of(&(0b0, value))
73}
74
75pub(crate) fn hash_branch<D>(lhs: &Hash<D>, rhs: &Hash<D>) -> Hash<D>
77where
78 D: SupportedDigest,
79{
80 Hash::of((0b1, lhs, rhs))
81}
82
83pub trait SupportedDigest: Digest + private::Sealed + Default + Sized + 'static {
84 const ALGORITHM: HashAlgorithm;
85 fn empty_tree_hash(height: usize) -> &'static Hash<Self>;
86}
87
88impl SupportedDigest for Sha256 {
89 const ALGORITHM: HashAlgorithm = HashAlgorithm::Sha256;
90 fn empty_tree_hash(height: usize) -> &'static Hash<Sha256> {
91 &EMPTY_TREE_HASH[height]
92 }
93}
94
95mod private {
96 use sha2::Sha256;
97
98 pub trait Sealed {}
99 impl Sealed for Sha256 {}
100}
101
102impl<D: SupportedDigest> From<Hash<D>> for AnyHash {
103 fn from(value: Hash<D>) -> Self {
104 (&value).into()
105 }
106}
107
108impl<D: SupportedDigest> From<&Hash<D>> for AnyHash {
109 fn from(value: &Hash<D>) -> Self {
110 AnyHash {
111 algo: D::ALGORITHM,
112 bytes: value.digest.to_vec(),
113 }
114 }
115}
116
117#[derive(Error, Debug)]
118pub enum HashError {
119 #[error("mismatched hash algorithm: expected {expected}, got {actual}")]
120 MismatchedAlgorithms {
121 expected: HashAlgorithm,
122 actual: HashAlgorithm,
123 },
124
125 #[error("expected {expected} bytes for hash algorithm {algo}, got {actual}")]
126 IncorrectLength {
127 expected: usize,
128 algo: HashAlgorithm,
129 actual: usize,
130 },
131}
132
133impl<D: SupportedDigest> TryFrom<AnyHash> for Hash<D> {
134 type Error = HashError;
135
136 fn try_from(value: AnyHash) -> Result<Self, Self::Error> {
137 if value.algorithm() == D::ALGORITHM {
138 let len = value.bytes.len();
139 match Hash::try_from(value.bytes) {
140 Ok(hash) => Ok(hash),
141 Err(IncorrectLengthError) => Err(HashError::IncorrectLength {
142 expected: <D as Digest>::output_size(),
143 algo: D::ALGORITHM,
144 actual: len,
145 }),
146 }
147 } else {
148 Err(HashError::MismatchedAlgorithms {
149 expected: D::ALGORITHM,
150 actual: value.algorithm(),
151 })
152 }
153 }
154}