kaspa_hashes/
hashers.rs

1// use sha3::CShake256;
2use once_cell::sync::Lazy;
3
4pub trait HasherBase {
5    fn update<A: AsRef<[u8]>>(&mut self, data: A) -> &mut Self;
6}
7
8pub trait Hasher: HasherBase + Clone + Default {
9    fn finalize(self) -> crate::Hash;
10    fn reset(&mut self);
11    #[inline(always)]
12    fn hash<A: AsRef<[u8]>>(data: A) -> crate::Hash {
13        let mut hasher = Self::default();
14        hasher.update(data);
15        hasher.finalize()
16    }
17}
18
19// Implemented manually in pow_hashers:
20//  struct PowHash => `cSHAKE256("ProofOfWorkHash")
21//  struct KHeavyHash => `cSHAKE256("HeavyHash")
22pub use crate::pow_hashers::{KHeavyHash, PowHash};
23blake2b_hasher! {
24    struct TransactionHash => b"TransactionHash",
25    struct TransactionID => b"TransactionID",
26    struct TransactionSigningHash => b"TransactionSigningHash",
27    struct BlockHash => b"BlockHash",
28    struct ProofOfWorkHash => b"ProofOfWorkHash",
29    struct MerkleBranchHash => b"MerkleBranchHash",
30    struct MuHashElementHash => b"MuHashElement",
31    struct MuHashFinalizeHash => b"MuHashFinalize",
32    struct PersonalMessageSigningHash => b"PersonalMessageSigningHash",
33}
34
35sha256_hasher! {
36    struct TransactionSigningHashECDSA => "TransactionSigningHashECDSA",
37}
38
39macro_rules! sha256_hasher {
40    ($(struct $name:ident => $domain_sep:literal),+ $(,)? ) => {$(
41        #[derive(Clone)]
42        pub struct $name(sha2::Sha256);
43
44        impl $name {
45            #[inline]
46            pub fn new() -> Self {
47                use sha2::{Sha256, Digest};
48                // We use Lazy in order to avoid rehashing it
49                // in the future we can replace this with the correct initial state.
50                static HASHER: Lazy<$name> = Lazy::new(|| {
51                    // SHA256 doesn't natively support domain separation, so we hash it to make it constant size.
52                    let mut tmp_state = Sha256::new();
53                    tmp_state.update($domain_sep);
54                    let mut out = $name(Sha256::new());
55                    out.write(tmp_state.finalize());
56
57                    out
58                });
59                (*HASHER).clone()
60            }
61
62            pub fn write<A: AsRef<[u8]>>(&mut self, data: A) {
63                sha2::Digest::update(&mut self.0, data.as_ref());
64            }
65
66            #[inline(always)]
67            pub fn finalize(self) -> crate::Hash {
68                let mut out = [0u8; 32];
69                out.copy_from_slice(sha2::Digest::finalize(self.0).as_slice());
70                crate::Hash(out)
71            }
72        }
73    impl_hasher!{ struct $name }
74    )*};
75}
76
77macro_rules! blake2b_hasher {
78    ($(struct $name:ident => $domain_sep:literal),+ $(,)? ) => {$(
79        #[derive(Clone)]
80        pub struct $name(blake2b_simd::State);
81
82        impl $name {
83            #[inline(always)]
84            pub fn new() -> Self {
85                Self(
86                    blake2b_simd::Params::new()
87                        .hash_length(32)
88                        .key($domain_sep)
89                        .to_state(),
90                )
91            }
92
93            pub fn write<A: AsRef<[u8]>>(&mut self, data: A) {
94                self.0.update(data.as_ref());
95            }
96
97            #[inline(always)]
98            pub fn finalize(self) -> crate::Hash {
99                let mut out = [0u8; 32];
100                out.copy_from_slice(self.0.finalize().as_bytes());
101                crate::Hash(out)
102            }
103        }
104    impl_hasher!{ struct $name }
105    )*};
106}
107macro_rules! impl_hasher {
108    (struct $name:ident) => {
109        impl HasherBase for $name {
110            #[inline(always)]
111            fn update<A: AsRef<[u8]>>(&mut self, data: A) -> &mut Self {
112                self.write(data);
113                self
114            }
115        }
116        impl Hasher for $name {
117            #[inline(always)]
118            fn finalize(self) -> crate::Hash {
119                // Call the method
120                $name::finalize(self)
121            }
122            #[inline(always)]
123            fn reset(&mut self) {
124                *self = Self::new();
125            }
126        }
127        impl Default for $name {
128            #[inline(always)]
129            fn default() -> Self {
130                Self::new()
131            }
132        }
133    };
134}
135
136use {blake2b_hasher, impl_hasher, sha256_hasher};
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[test]
143    fn test_vectors() {
144        let input_data = [
145            &[],
146            &[1][..],
147            &[
148                5, 199, 126, 44, 71, 32, 82, 139, 122, 217, 43, 48, 52, 112, 40, 209, 180, 83, 139, 231, 72, 48, 136, 48, 168, 226,
149                133, 7, 60, 4, 160, 205,
150            ][..],
151            &[42; 64],
152            &[0; 8][..],
153        ];
154
155        fn run_test_vector<H: Hasher>(input_data: &[&[u8]], hasher_new: impl FnOnce() -> H, expected: &[&str]) {
156            let mut hasher = hasher_new();
157            // We do not reset the hasher each time on purpose, this also tests incremental hashing.
158            for (data, expected) in input_data.iter().zip(expected) {
159                println!("data: {data:?}");
160                let hash = hasher.update(data).clone().finalize();
161                assert_eq!(hash.to_string(), *expected, "Type: {}", std::any::type_name::<H>());
162            }
163        }
164
165        run_test_vector(
166            &input_data,
167            TransactionHash::new,
168            &[
169                "50272a9e37c728026f93d0eda6ab4467f627338b879076483c88d291193cb3bf",
170                "f9bf7e04c712621a0f4bb75d763f9ef5f73af6c438fd15b80744393bc96398ad",
171                "8e791f3edcc92b71b8de2778efbc4666ee5bd146acbe8723a55bca26b022b0e0",
172                "a6dab1a3088548c62d13a082fa28e870fdbbe51adcd8c364e2ea37e473c04d81",
173                "3b79b78b967233843ad30f707b165eb3d6a91af8338076be8755c46a963c3d1d",
174            ],
175        );
176        run_test_vector(
177            &input_data,
178            TransactionID::new,
179            &[
180                "e5f65efda0894d2b0590c2e9e46e9acc03032f505a1522f5e8c78c5ec70b1d9c",
181                "aea52cf5e5a13da13a52dd69abd636eb1b0f86e58bc1dda6b17886b94593415a",
182                "a50a2f87bdce075740189e9e23907ae22b5addbd875ccb70c116811b1fa5fb18",
183                "0db7a485f7013a346a8f7f5caf73d52ca3c3b5ee101ad8753adedd4235b7236b",
184                "2afc9c855854b0a6e94a722c3451d0cdfc8c11748b78ef65b9786f87b48d0d07",
185            ],
186        );
187
188        run_test_vector(
189            &input_data,
190            TransactionSigningHash::new,
191            &[
192                "34c75037ad62740d4b3228f88f844f7901c07bfacd55a045be518eabc15e52ce",
193                "8523b0471bcbea04575ccaa635eef9f9114f2890bda54367e5ff8caa3878bf82",
194                "a51c49d9eb3d13f9de16e1aa8d1ff17668d55633ce00f36a643ac714b0fb137f",
195                "487f199ef74c3e893e85bd37770e6334575a2d4d113b2e10474593c49807de93",
196                "6392adc33a8e24e9a0a0c4c5f07f9c1cc958ad40c16d7a9a276e374cebb4e32b",
197            ],
198        );
199        run_test_vector(
200            &input_data,
201            TransactionSigningHashECDSA::new,
202            &[
203                "b31ad1fbbe41b0e2a90e07c84708b38ba581f0c0e9185416913a04fb6d342027",
204                "c43e1f75ea9df6379b56a95074c2b6289ed8c5a01fff2d49d9d44ad5575c164b",
205                "49085f99fa0084b5436663f757a5916b1e4290c3321707fb76921ed4e47844ec",
206                "3f887e866428de813c1d0463b14eef3ca1363c8187e917dda1eee0ec5996490b",
207                "56de89a8c75f0fee2de61b11ab05d0d42e29ed50879467cf128dd80800a52ada",
208            ],
209        );
210
211        run_test_vector(
212            &input_data,
213            BlockHash::new,
214            &[
215                "a80b6aa20f20b15ebabe2b1949527f78a257594a732e774de637d85e6973a768",
216                "5643023add641f9421187b8c9aa3c6c73227d5ec34131c61a08d35b43e7e4b65",
217                "4dc3bf72045431e46f8839a7d390898f27c887fddd8637149bfb70f732f04334",
218                "15d7648e69023dca65c949a61ea166192049f449c604523494813873b19918a7",
219                "3ac41af8385ea5d902ce6d47f509b7accc9c631f1d57a719d777874467f6d877",
220            ],
221        );
222
223        run_test_vector(
224            &input_data,
225            MerkleBranchHash::new,
226            &[
227                "4de3617db456d01248173f17ec58196e92fbd994b636476db4b875ed2ec84054",
228                "5737cd8b6fca5a30c19a491323a14e6b7021641cb3f8875f10c7a2eafd3cf43f",
229                "a49eeda61cc75e0a8e5915829752fe0ad97620d6d32de7c9883595b0810ca33e",
230                "28f33681dcff1313674e07dacc2d74c3089f6d8cea7a4f8792a71fd870988ee5",
231                "2d53a43a42020a5091c125230bcd8a4cf0eeb188333e68325d4bce58a1c75ca3",
232            ],
233        );
234    }
235}