1use alloc::vec::Vec;
8use digest::Digest;
9use digest::typenum::U32;
10
11use crate::BYTES_PER_CHUNK;
12
13#[inline]
15pub fn hash_pair<D: Digest<OutputSize = U32>>(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
16 let mut hasher = D::new();
17 hasher.update(left);
18 hasher.update(right);
19 let out = hasher.finalize();
20 let mut arr = [0u8; 32];
21 arr.copy_from_slice(out.as_slice());
22 arr
23}
24
25pub fn pack_bytes(bytes: &[u8]) -> Vec<[u8; 32]> {
27 if bytes.is_empty() {
28 return Vec::new();
29 }
30 let n = bytes.len().div_ceil(BYTES_PER_CHUNK);
31 let mut out = Vec::with_capacity(n);
32 let mut cursor = 0;
33 for _ in 0..n {
34 let mut chunk = [0u8; 32];
35 let take = core::cmp::min(BYTES_PER_CHUNK, bytes.len() - cursor);
36 chunk[..take].copy_from_slice(&bytes[cursor..cursor + take]);
37 out.push(chunk);
38 cursor += take;
39 }
40 out
41}
42
43#[inline]
45pub fn leaves_at_depth(depth: usize) -> u64 {
46 1u64 << depth
47}
48
49#[inline]
51pub fn ceil_log2(n: u64) -> usize {
52 if n <= 1 {
53 return 0;
54 }
55 (64 - (n - 1).leading_zeros()) as usize
56}
57
58pub fn merkleize<D: Digest<OutputSize = U32>>(chunks: &[[u8; 32]], limit: usize) -> [u8; 32] {
74 let target = core::cmp::max(limit, chunks.len()).max(1);
75 let padded_len = target.next_power_of_two();
76 let depth = padded_len.trailing_zeros() as usize;
77
78 if padded_len == 1 {
79 return chunks.first().copied().unwrap_or([0u8; 32]);
80 }
81
82 if chunks.is_empty() {
84 return zero_hash::<D>(depth);
85 }
86
87 let mut zero_h_table: Vec<[u8; 32]> = Vec::with_capacity(depth);
94 let mut cur_zero = [0u8; 32];
95 zero_h_table.push(cur_zero);
96 for _ in 1..depth {
97 cur_zero = hash_pair::<D>(&cur_zero, &cur_zero);
98 zero_h_table.push(cur_zero);
99 }
100
101 let mut level: Vec<[u8; 32]> = Vec::new();
106 level.extend_from_slice(chunks);
107
108 for &zero_h in zero_h_table.iter().take(depth) {
109 let next_count = level.len().div_ceil(2);
110 let mut next: Vec<[u8; 32]> = Vec::with_capacity(next_count);
111 for i in 0..next_count {
112 let l = level[2 * i];
113 let r = level.get(2 * i + 1).copied().unwrap_or(zero_h);
114 next.push(hash_pair::<D>(&l, &r));
115 }
116 level = next;
117 }
118
119 level[0]
120}
121
122#[inline]
124pub fn mix_in_length<D: Digest<OutputSize = U32>>(root: [u8; 32], len: u64) -> [u8; 32] {
125 let mut buf = [0u8; 32];
126 buf[..8].copy_from_slice(&len.to_le_bytes());
127 hash_pair::<D>(&root, &buf)
128}
129
130#[inline]
135pub fn mix_in_selector<D: Digest<OutputSize = U32>>(root: [u8; 32], selector: u8) -> [u8; 32] {
136 let mut buf = [0u8; 32];
137 buf[0] = selector;
138 hash_pair::<D>(&root, &buf)
139}
140
141pub fn zero_hash<D: Digest<OutputSize = U32>>(depth: usize) -> [u8; 32] {
144 let mut current = [0u8; 32];
150 for _ in 0..depth {
151 current = hash_pair::<D>(¤t, ¤t);
152 }
153 current
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use sha2::Sha256;
160
161 #[test]
162 fn zero_hash_depth_0_is_zero() {
163 assert_eq!(zero_hash::<Sha256>(0), [0u8; 32]);
164 }
165
166 #[test]
167 fn zero_hash_depth_1_is_h00() {
168 let expected = hash_pair::<Sha256>(&[0u8; 32], &[0u8; 32]);
169 assert_eq!(zero_hash::<Sha256>(1), expected);
170 }
171
172 #[test]
173 fn merkleize_single_chunk_returns_chunk() {
174 let chunk = [0xAAu8; 32];
175 assert_eq!(merkleize::<Sha256>(&[chunk], 1), chunk);
176 }
177
178 #[test]
179 fn merkleize_two_chunks() {
180 let a = [0xAAu8; 32];
181 let b = [0xBBu8; 32];
182 let expected = hash_pair::<Sha256>(&a, &b);
183 assert_eq!(merkleize::<Sha256>(&[a, b], 2), expected);
184 }
185
186 #[test]
187 fn merkleize_empty_with_limit_zero_is_zero_chunk() {
188 assert_eq!(merkleize::<Sha256>(&[], 0), [0u8; 32]);
189 }
190
191 #[test]
192 fn merkleize_padded_with_zero_hashes() {
193 let a = [1u8; 32];
195 let b = [2u8; 32];
196 let c = [3u8; 32];
197 let left = hash_pair::<Sha256>(&a, &b);
198 let right = hash_pair::<Sha256>(&c, &[0u8; 32]);
199 let root = hash_pair::<Sha256>(&left, &right);
200 assert_eq!(merkleize::<Sha256>(&[a, b, c], 4), root);
201 }
202
203 #[test]
204 fn pack_bytes_pads_tail_with_zeros() {
205 let chunks = pack_bytes(&[1, 2, 3]);
206 assert_eq!(chunks.len(), 1);
207 let mut expected = [0u8; 32];
208 expected[0] = 1;
209 expected[1] = 2;
210 expected[2] = 3;
211 assert_eq!(chunks[0], expected);
212 }
213
214 #[test]
215 fn merkleize_huge_limit_finishes_fast() {
216 let chunk = [0xAAu8; 32];
221 let root = merkleize::<Sha256>(&[chunk], 1usize << 32);
222
223 let mut current = chunk;
226 for d in 0..32 {
227 current = hash_pair::<Sha256>(¤t, &zero_hash::<Sha256>(d));
228 }
229 assert_eq!(root, current);
230 }
231
232 #[test]
233 fn merkleize_empty_with_large_limit_is_zero_subtree() {
234 let root = merkleize::<Sha256>(&[], 1024);
237 assert_eq!(root, zero_hash::<Sha256>(10));
238 }
239
240 #[test]
241 fn merkleize_three_chunks_with_large_limit() {
242 let a = [1u8; 32];
244 let b = [2u8; 32];
245 let c = [3u8; 32];
246 let root = merkleize::<Sha256>(&[a, b, c], 16);
247
248 let l1_0 = hash_pair::<Sha256>(&a, &b);
252 let l1_1 = hash_pair::<Sha256>(&c, &zero_hash::<Sha256>(0));
253 let l2_0 = hash_pair::<Sha256>(&l1_0, &l1_1);
254 let l3_0 = hash_pair::<Sha256>(&l2_0, &zero_hash::<Sha256>(2));
255 let l4_0 = hash_pair::<Sha256>(&l3_0, &zero_hash::<Sha256>(3));
256 assert_eq!(root, l4_0);
257 }
258
259 #[test]
260 fn ceil_log2_examples() {
261 assert_eq!(ceil_log2(0), 0);
262 assert_eq!(ceil_log2(1), 0);
263 assert_eq!(ceil_log2(2), 1);
264 assert_eq!(ceil_log2(3), 2);
265 assert_eq!(ceil_log2(4), 2);
266 assert_eq!(ceil_log2(5), 3);
267 assert_eq!(ceil_log2(8), 3);
268 assert_eq!(ceil_log2(9), 4);
269 }
270}