1use alloc::vec::Vec;
9
10pub trait Hash {
15 type Out: Copy + Eq + core::fmt::Debug;
17
18 fn hash(bytes: &[u8]) -> Self::Out;
20
21 fn hash_pair(a: &[u8], b: &[u8]) -> Self::Out {
25 let mut buf = Vec::with_capacity(a.len() + b.len());
26 buf.extend_from_slice(a);
27 buf.extend_from_slice(b);
28 Self::hash(&buf)
29 }
30}
31
32pub struct Blake2b256;
34
35impl Hash for Blake2b256 {
36 type Out = [u8; 32];
37
38 fn hash(bytes: &[u8]) -> Self::Out {
39 use blake2::digest::{Update, VariableOutput};
40 let mut hasher = blake2::Blake2bVar::new(32).expect("32 ≤ Blake2b max output");
41 hasher.update(bytes);
42 let mut out = [0u8; 32];
43 hasher.finalize_variable(&mut out).expect("32-byte buffer");
44 out
45 }
46
47 fn hash_pair(a: &[u8], b: &[u8]) -> Self::Out {
48 use blake2::digest::{Update, VariableOutput};
49 let mut hasher = blake2::Blake2bVar::new(32).expect("32 ≤ Blake2b max output");
50 hasher.update(a);
51 hasher.update(b);
52 let mut out = [0u8; 32];
53 hasher.finalize_variable(&mut out).expect("32-byte buffer");
54 out
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn empty_hash_is_deterministic() {
64 let h1 = Blake2b256::hash(b"");
65 let h2 = Blake2b256::hash(b"");
66 assert_eq!(h1, h2);
67 }
68
69 #[test]
70 fn different_inputs_different_hashes() {
71 assert_ne!(Blake2b256::hash(b"hello"), Blake2b256::hash(b"world"));
72 assert_ne!(Blake2b256::hash(b"a"), Blake2b256::hash(b"b"));
73 }
74
75 #[test]
76 fn hash_pair_matches_hash_of_concat() {
77 let a: &[u8] = b"foo";
78 let b: &[u8] = b"barbaz";
79 let mut joined = Vec::new();
80 joined.extend_from_slice(a);
81 joined.extend_from_slice(b);
82 assert_eq!(Blake2b256::hash_pair(a, b), Blake2b256::hash(&joined));
83 }
84
85 #[test]
86 fn hash_pair_empty_matches_single() {
87 let a: &[u8] = b"";
88 let b: &[u8] = b"only";
89 assert_eq!(Blake2b256::hash_pair(a, b), Blake2b256::hash(b"only"));
90 }
91}