Skip to main content

kaspa_hashes/
hashers.rs

1// use sha3::CShake256;
2
3pub trait HasherBase {
4    fn update<A: AsRef<[u8]>>(&mut self, data: A) -> &mut Self;
5}
6
7pub trait Hasher: HasherBase + Clone + Default {
8    fn finalize(self) -> crate::Hash;
9    fn reset(&mut self);
10    #[inline(always)]
11    fn hash<A: AsRef<[u8]>>(data: A) -> crate::Hash {
12        let mut hasher = Self::default();
13        hasher.update(data);
14        hasher.finalize()
15    }
16}
17
18// Implemented manually in pow_hashers:
19//  struct PowHash => `cSHAKE256("ProofOfWorkHash")
20//  struct KHeavyHash => `cSHAKE256("HeavyHash")
21pub use crate::pow_hashers::{KHeavyHash, PowHash};
22blake2b_hasher! {
23    struct TransactionHash => b"TransactionHash",
24    struct TransactionID => b"TransactionID",
25    struct TransactionSigningHash => b"TransactionSigningHash",
26    struct BlockHash => b"BlockHash",
27    struct ProofOfWorkHash => b"ProofOfWorkHash",
28    struct MerkleBranchHash => b"MerkleBranchHash",
29    struct MuHashElementHash => b"MuHashElement",
30    struct MuHashFinalizeHash => b"MuHashFinalize",
31    struct PersonalMessageSigningHash => b"PersonalMessageSigningHash",
32    struct CovenantID => b"CovenantID",
33}
34
35sha256_hasher! {
36    struct TransactionSigningHashECDSA => b"TransactionSigningHashECDSA",
37}
38
39blake3_hasher! {
40    struct SeqCommitMerkleBranch => b"SeqCommitmentMerkleBranchHash",
41    struct PayloadDigest => b"PayloadDigest",
42    struct TransactionRest => b"TransactionRest",
43    struct TransactionV1Id => b"TransactionV1Id",
44    struct SeqCommitLaneKey => b"SeqCommitLaneKey",
45    struct SeqCommitLaneTip => b"SeqCommitLaneTip",
46    struct SeqCommitActivityLeaf => b"SeqCommitActivityLeaf",
47    struct SeqCommitMergesetContext => b"SeqCommitMergesetContext",
48    struct SeqCommitMinerPayloadLeaf => b"SeqCommitMinerPayloadLeaf",
49    struct SeqCommitActivityRoot => b"SeqCommitActivityRoot",
50
51    struct SeqCommitActiveLeaf => b"SeqCommitActiveLeaf",
52    struct SeqCommitActiveNode => b"SeqCommitActiveNode",
53    struct SeqCommitActiveCollapsedNode => b"SeqCommitActiveCollapsedNode",
54}
55
56#[macro_export]
57macro_rules! sha256_hasher {
58    ($(struct $name:ident => $domain_sep:literal),+ $(,)? ) => {$(
59        #[derive(Clone)]
60        pub struct $name($crate::sha2::Sha256);
61
62        impl $name {
63            #[inline]
64            pub fn new() -> Self {
65                use $crate::sha2::{Digest as _};
66                const DOMAIN_HASH: [u8; 32] =  {
67                    $crate::sha2_const_stable::Sha256::new().update($domain_sep).finalize()
68                };
69                Self($crate::sha2::Sha256::new_with_prefix(DOMAIN_HASH))
70            }
71
72            pub fn write<A: AsRef<[u8]>>(&mut self, data: A) {
73                $crate::sha2::Digest::update(&mut self.0, data.as_ref());
74            }
75
76            #[inline(always)]
77            pub fn finalize(self) -> $crate::Hash {
78                let mut out = [0u8; 32];
79                out.copy_from_slice($crate::sha2::Digest::finalize(self.0).as_slice());
80                $crate::Hash::from_bytes(out)
81            }
82        }
83    $crate::impl_hasher!{ struct $name }
84    )*};
85}
86
87#[macro_export]
88macro_rules! blake2b_hasher {
89    ($(struct $name:ident => $domain_sep:literal),+ $(,)? ) => {$(
90        #[derive(Clone)]
91        pub struct $name($crate::blake2b_simd::State);
92
93        impl $name {
94            #[inline(always)]
95            pub fn new() -> Self {
96                Self(
97                    $crate::blake2b_simd::Params::new()
98                        .hash_length(32)
99                        .key($domain_sep)
100                        .to_state(),
101                )
102            }
103
104            pub fn write<A: AsRef<[u8]>>(&mut self, data: A) {
105                self.0.update(data.as_ref());
106            }
107
108            #[inline(always)]
109            pub fn finalize(self) -> $crate::Hash {
110                let mut out = [0u8; 32];
111                out.copy_from_slice(self.0.finalize().as_bytes());
112                $crate::Hash::from_bytes(out)
113            }
114        }
115    $crate::impl_hasher!{ struct $name }
116    )*};
117}
118
119#[macro_export]
120macro_rules! blake3_hasher {
121    ($(struct $name:ident => $domain_sep:literal),+ $(,)? ) => {$(
122        #[derive(Clone)]
123        pub struct $name($crate::blake3::Hasher);
124
125        impl $name {
126            #[inline(always)]
127            pub fn new() -> Self {
128                const KEY: [u8; $crate::blake3::KEY_LEN] = {
129                    let mut key = [0u8; $crate::blake3::KEY_LEN];
130                    let mut i = 0usize;
131                    while i < $domain_sep.len() {
132                        key[i] = $domain_sep[i];
133                        i += 1;
134                    }
135                    key
136                };
137
138                Self($crate::blake3::Hasher::new_keyed(&KEY))
139            }
140
141            pub fn write<A: AsRef<[u8]>>(&mut self, data: A) {
142                self.0.update(data.as_ref());
143            }
144
145            #[inline(always)]
146            pub fn finalize(self) -> $crate::Hash {
147                let mut out = [0u8; 32];
148                out.copy_from_slice(self.0.finalize().as_bytes());
149                $crate::Hash::from_bytes(out)
150            }
151        }
152    $crate::impl_hasher!{ struct $name }
153    )*};
154}
155
156#[macro_export]
157macro_rules! impl_hasher {
158    (struct $name:ident) => {
159        impl $crate::HasherBase for $name {
160            #[inline(always)]
161            fn update<A: AsRef<[u8]>>(&mut self, data: A) -> &mut Self {
162                self.write(data);
163                self
164            }
165        }
166        impl $crate::Hasher for $name {
167            #[inline(always)]
168            fn finalize(self) -> $crate::Hash {
169                // Call the method
170                $name::finalize(self)
171            }
172            #[inline(always)]
173            fn reset(&mut self) {
174                *self = Self::new();
175            }
176        }
177        impl Default for $name {
178            #[inline(always)]
179            fn default() -> Self {
180                Self::new()
181            }
182        }
183    };
184}
185
186use {blake2b_hasher, blake3_hasher, sha256_hasher};
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191    use std::println;
192    use std::string::ToString;
193
194    fn run_test_vector<H: Hasher>(input_data: &[&[u8]], hasher_new: impl FnOnce() -> H, expected: &[&str]) {
195        let mut hasher = hasher_new();
196        // We do not reset the hasher each time on purpose, this also tests incremental hashing.
197        for (data, expected) in input_data.iter().zip(expected) {
198            println!("data: {data:?}");
199            let hash = hasher.update(data).clone().finalize();
200            assert_eq!(hash.to_string(), *expected, "Type: {}", std::any::type_name::<H>());
201        }
202    }
203
204    #[test]
205    fn test_vectors() {
206        let input_data = [
207            &[],
208            &[1][..],
209            &[
210                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,
211                133, 7, 60, 4, 160, 205,
212            ][..],
213            &[42; 64],
214            &[0; 8][..],
215        ];
216
217        run_test_vector(
218            &input_data,
219            TransactionHash::new,
220            &[
221                "50272a9e37c728026f93d0eda6ab4467f627338b879076483c88d291193cb3bf",
222                "f9bf7e04c712621a0f4bb75d763f9ef5f73af6c438fd15b80744393bc96398ad",
223                "8e791f3edcc92b71b8de2778efbc4666ee5bd146acbe8723a55bca26b022b0e0",
224                "a6dab1a3088548c62d13a082fa28e870fdbbe51adcd8c364e2ea37e473c04d81",
225                "3b79b78b967233843ad30f707b165eb3d6a91af8338076be8755c46a963c3d1d",
226            ],
227        );
228        run_test_vector(
229            &input_data,
230            TransactionID::new,
231            &[
232                "e5f65efda0894d2b0590c2e9e46e9acc03032f505a1522f5e8c78c5ec70b1d9c",
233                "aea52cf5e5a13da13a52dd69abd636eb1b0f86e58bc1dda6b17886b94593415a",
234                "a50a2f87bdce075740189e9e23907ae22b5addbd875ccb70c116811b1fa5fb18",
235                "0db7a485f7013a346a8f7f5caf73d52ca3c3b5ee101ad8753adedd4235b7236b",
236                "2afc9c855854b0a6e94a722c3451d0cdfc8c11748b78ef65b9786f87b48d0d07",
237            ],
238        );
239
240        run_test_vector(
241            &input_data,
242            TransactionSigningHash::new,
243            &[
244                "34c75037ad62740d4b3228f88f844f7901c07bfacd55a045be518eabc15e52ce",
245                "8523b0471bcbea04575ccaa635eef9f9114f2890bda54367e5ff8caa3878bf82",
246                "a51c49d9eb3d13f9de16e1aa8d1ff17668d55633ce00f36a643ac714b0fb137f",
247                "487f199ef74c3e893e85bd37770e6334575a2d4d113b2e10474593c49807de93",
248                "6392adc33a8e24e9a0a0c4c5f07f9c1cc958ad40c16d7a9a276e374cebb4e32b",
249            ],
250        );
251        run_test_vector(
252            &input_data,
253            TransactionSigningHashECDSA::new,
254            &[
255                "b31ad1fbbe41b0e2a90e07c84708b38ba581f0c0e9185416913a04fb6d342027",
256                "c43e1f75ea9df6379b56a95074c2b6289ed8c5a01fff2d49d9d44ad5575c164b",
257                "49085f99fa0084b5436663f757a5916b1e4290c3321707fb76921ed4e47844ec",
258                "3f887e866428de813c1d0463b14eef3ca1363c8187e917dda1eee0ec5996490b",
259                "56de89a8c75f0fee2de61b11ab05d0d42e29ed50879467cf128dd80800a52ada",
260            ],
261        );
262
263        run_test_vector(
264            &input_data,
265            BlockHash::new,
266            &[
267                "a80b6aa20f20b15ebabe2b1949527f78a257594a732e774de637d85e6973a768",
268                "5643023add641f9421187b8c9aa3c6c73227d5ec34131c61a08d35b43e7e4b65",
269                "4dc3bf72045431e46f8839a7d390898f27c887fddd8637149bfb70f732f04334",
270                "15d7648e69023dca65c949a61ea166192049f449c604523494813873b19918a7",
271                "3ac41af8385ea5d902ce6d47f509b7accc9c631f1d57a719d777874467f6d877",
272            ],
273        );
274
275        run_test_vector(
276            &input_data,
277            MerkleBranchHash::new,
278            &[
279                "4de3617db456d01248173f17ec58196e92fbd994b636476db4b875ed2ec84054",
280                "5737cd8b6fca5a30c19a491323a14e6b7021641cb3f8875f10c7a2eafd3cf43f",
281                "a49eeda61cc75e0a8e5915829752fe0ad97620d6d32de7c9883595b0810ca33e",
282                "28f33681dcff1313674e07dacc2d74c3089f6d8cea7a4f8792a71fd870988ee5",
283                "2d53a43a42020a5091c125230bcd8a4cf0eeb188333e68325d4bce58a1c75ca3",
284            ],
285        );
286    }
287
288    #[test]
289    fn test_blake3_vectors() {
290        let input_data = [
291            &[][..],
292            &[1][..],
293            &[
294                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,
295                133, 7, 60, 4, 160, 205,
296            ][..],
297            &[42; 64],
298            &[0; 8][..],
299        ];
300
301        run_test_vector(
302            &input_data,
303            SeqCommitLaneKey::new,
304            &[
305                "ffb48ccf5a7d24d449a8da0dd09a73b1e0a20ee10a7331c21930042e58d2aa24",
306                "4daeb9c121484df93175020a60c04463b3c8b6ccfa4a06b7a8e6aff9ace7cfa4",
307                "41a1aa1d34375a2ef257571a8ca52c73957483811cb4852a3b815fd72b3d773c",
308                "a240e37f30a735d5791e589f4ca193e3ec801e24565f2f797faec33f88c10f9e",
309                "a2df04afbd137a8ee9ed44a001af5dcc98cfffd14100163ebd2f899c542127a0",
310            ],
311        );
312        run_test_vector(
313            &input_data,
314            SeqCommitLaneTip::new,
315            &[
316                "255d4f981ed1e613ec87d46750254527c3044e42ce6b639dfa4190ab3965d777",
317                "274025031a8f9b81346e0f1e0cd215929736c89bcf3b3589b744b196663dbf02",
318                "72790c6e65fda13ddf64d48c062ad0f18d218f8a8c83c9426d13911ba60da4e6",
319                "3359cef7e345290fa4f5270849ecdf154efcb06b189b7fbcf6889691cda5d3eb",
320                "932c73359760c7f04ef38be45dd863b7a31372756c7f34ffec263edbc6b75362",
321            ],
322        );
323        run_test_vector(
324            &input_data,
325            SeqCommitActivityLeaf::new,
326            &[
327                "925e8cc9643d7b2ae93e38f7a0022f1f0c694eeb6608d991389f77be5f3dab3e",
328                "de3a34c353bcd4dc1be835c5412d6fb3bcb55801750ce68f07ccd25d5d6446ee",
329                "65abaa78c27d5400a4d28df2d6d6219939f4b4973012f651086fbef6f4159346",
330                "5d3121321c6baaaec650f3746a1aea65b3310d1d544e8dc6a2d93ca6b0baf3c3",
331                "dab38f396da66f90cedd52e3cf7ccf6846756cb1693465ef93791c657f8fc868",
332            ],
333        );
334        run_test_vector(
335            &input_data,
336            SeqCommitMergesetContext::new,
337            &[
338                "cd2b97a22304873dfa189e15c0a13636ea60570c11f763c6c1c6efc49d469489",
339                "6cf9af3a32964ffea8b4eccd2cd2002d5b92cdb5ac11fea6ec1dfe229ddf652c",
340                "55fa8db49590d0ef4f3d36e9c0b3b41e6791eaf8eda968f3499e66d952766e6e",
341                "394d104cdb22f9429374dd15b6cb2e601876e6b97985803da9837a15d43aab42",
342                "c8c5d211df896199dc349aeadc6da0fe3c3375f3539d1936268f9a6c64b6ed39",
343            ],
344        );
345        run_test_vector(
346            &input_data,
347            PayloadDigest::new,
348            &[
349                "9c0ca2acb45e92ffe6ceb4ae29188b35c82d9676cdd3ce067fd6ccc30a9c4a38",
350                "a69883bfb2bb000249639d8bbf5019fd08ceb7daa7a86f170530522dfedba124",
351                "04d7abe0495ff2c520f93ed31bb37f58a9375abb48d50b163864335af8d834de",
352                "5a4aa21c7235ec3edb7bd198b576f78cca6c5027dd7938ed41e74ae982858021",
353                "d6e0e23e9130b0e44734f3639489e22e7f7d928f474386599db6fca3b02d6295",
354            ],
355        );
356        run_test_vector(
357            &input_data,
358            SeqCommitMinerPayloadLeaf::new,
359            &[
360                "1f53866c878ac122fe24c45ae3dd7931d9d045559f53ee64512aabf9dc1a0892",
361                "4f0987631ad1a5a7b47c44309fc9690a5a1bab2351807d1282c6dabe1b06b235",
362                "5dd4ed4f4a7fbc7054ec04df67f2acc8db259ca045614cb5390f6dc1b5a8275f",
363                "fd4f1c71ecdff8ac4448a4ebccc540e6f30705c214ea62944bd41498e0409ed7",
364                "b870bed32130d2e8a9697b4f205d9c9ddc26d4adcb5cf9df666acea8f071bb44",
365            ],
366        );
367        run_test_vector(
368            &input_data,
369            SeqCommitActiveLeaf::new,
370            &[
371                "9bccebe22e721372272c9d8520d2b3654bb93db82f41d77d460b682d97de1120",
372                "3fde1c896d32bf8d24cff1ed22df7f5a76c10b45b37b5f836cdbbe3a9fe28a58",
373                "a603c38c61e68d8fa7020cd1d4d6b07658985863192d589d0e6cffaa706e67cd",
374                "35a96a743bc1521c7f5cc15968a407a82270c7b888fa0e4389d95afdbf0b0637",
375                "4fd0302a50d9b4394a8db8bfb7e92d29f5731c89bb7e0523ebafed0098af76c2",
376            ],
377        );
378        run_test_vector(
379            &input_data,
380            SeqCommitActiveNode::new,
381            &[
382                "6e1c8cc1d31bca65366d150b2d6ce3fff9436f5fb9549bb5f4e656c3e79bb385",
383                "833ff5ffa0262a58ff7a062d1d8ad5f943b8433648b6699a7db4bfb144490c3b",
384                "e4ba2f5b76d7385deeb8ca384c83ba47457bcdec14508f73ffb4c9a359c8c5b7",
385                "ece4a35bea3f577e961f8a3cd6f6e140013166cd045cbc12a60e6029713a286b",
386                "d66a35eca6ea9be29cbf16fa3e1da9d6550f62ae6db9d54f00a21f8315723594",
387            ],
388        );
389    }
390}