nectar_primitives/bmt/
hasher.rs1use alloy_primitives::{B256, Keccak256};
7use bytes::Bytes;
8use digest::{FixedOutput, FixedOutputReset, OutputSizeUser, Reset, Update};
9use hybrid_array::{Array, sizes::U32};
10use std::io::{self, Write};
11use std::sync::LazyLock;
12
13#[cfg(not(target_arch = "wasm32"))]
15use rayon;
16
17use super::constants::*;
18
19const ZERO_TREE_LEVELS: usize = zero_tree_levels(DEFAULT_BODY_SIZE);
21
22static ZERO_HASHES: LazyLock<[B256; ZERO_TREE_LEVELS]> = LazyLock::new(|| {
24 let mut hashes = [B256::ZERO; ZERO_TREE_LEVELS];
25
26 let mut hasher = Keccak256::new();
28 hasher.update([0u8; SEGMENT_PAIR_LENGTH]);
29 hashes[0] = B256::from_slice(hasher.finalize().as_slice());
30
31 for i in 1..ZERO_TREE_LEVELS {
33 let mut hasher = Keccak256::new();
34 hasher.update(hashes[i - 1].as_slice());
35 hasher.update(hashes[i - 1].as_slice());
36 hashes[i] = B256::from_slice(hasher.finalize().as_slice());
37 }
38
39 hashes
40});
41
42#[derive(Debug, Clone)]
44pub struct Hasher<const BODY_SIZE: usize = DEFAULT_BODY_SIZE> {
45 span: u64,
46 prefix: Option<Vec<u8>>,
47 buffer: [u8; BODY_SIZE],
48 cursor: usize,
49}
50
51impl<const BODY_SIZE: usize> Default for Hasher<BODY_SIZE> {
52 #[inline]
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl<const BODY_SIZE: usize> Hasher<BODY_SIZE> {
59 #[inline]
61 pub const fn new() -> Self {
62 Self {
63 span: 0,
64 prefix: None,
65 buffer: [0u8; BODY_SIZE],
66 cursor: 0,
67 }
68 }
69
70 #[inline]
72 pub const fn set_span(&mut self, span: u64) {
73 self.span = span;
74 }
75
76 #[inline(always)]
78 pub const fn span(&self) -> u64 {
79 self.span
80 }
81
82 #[inline]
84 pub fn prefix_with(&mut self, prefix: &[u8]) {
85 self.prefix = Some(prefix.to_vec());
86 }
87
88 #[inline(always)]
90 pub fn prefix(&self) -> &[u8] {
91 self.prefix.as_deref().unwrap_or(&[])
92 }
93
94 #[inline(always)]
96 pub const fn position(&self) -> usize {
97 self.cursor
98 }
99
100 #[inline(always)]
102 pub const fn len(&self) -> usize {
103 self.cursor
104 }
105
106 #[inline(always)]
108 pub const fn is_empty(&self) -> bool {
109 self.cursor == 0
110 }
111
112 #[inline]
114 pub fn update(&mut self, data: &[u8]) {
115 if data.is_empty() {
116 return;
117 }
118
119 let available_space = BODY_SIZE - self.cursor;
121 let bytes_to_copy = data.len().min(available_space);
122
123 if bytes_to_copy > 0 {
124 self.buffer[self.cursor..self.cursor + bytes_to_copy]
126 .copy_from_slice(&data[..bytes_to_copy]);
127
128 self.cursor += bytes_to_copy;
130 }
131 }
132
133 #[allow(clippy::should_implement_trait)] #[inline]
136 pub fn hash(&self, out: &mut [u8]) {
137 let hash = self.sum();
138 out.copy_from_slice(hash.as_slice());
139 }
140
141 #[inline]
143 #[must_use]
144 pub fn sum(&self) -> B256 {
145 self.finalize_with_prefix(self.hash_internal())
146 }
147
148 #[inline(always)]
151 fn is_all_zeros(data: &[u8]) -> bool {
152 data.iter().fold(0u8, |acc, &b| acc | b) == 0
155 }
156
157 #[inline(always)]
164 fn hash_internal(&self) -> B256 {
165 if self.cursor == 0 {
167 return ZERO_HASHES[ZERO_TREE_LEVELS - 1];
168 }
169
170 if Self::is_all_zeros(&self.buffer[..self.cursor]) {
173 return ZERO_HASHES[ZERO_TREE_LEVELS - 1];
174 }
175
176 let effective_size = self
178 .cursor
179 .next_power_of_two()
180 .max(SEGMENT_PAIR_LENGTH)
181 .min(BODY_SIZE);
182
183 #[cfg(not(target_arch = "wasm32"))]
185 let mut result = self.hash_subtree_parallel(&self.buffer[..effective_size], effective_size);
186
187 #[cfg(target_arch = "wasm32")]
188 let mut result =
189 self.hash_subtree_sequential(&self.buffer[..effective_size], effective_size);
190
191 let mut current_size = effective_size;
193 while current_size < BODY_SIZE {
194 let sibling_level = Self::zero_tree_level(current_size);
196 let mut hasher = Keccak256::new();
197 hasher.update(result.as_slice());
198 hasher.update(ZERO_HASHES[sibling_level].as_slice());
199 result = B256::from_slice(hasher.finalize().as_slice());
200 current_size *= 2;
201 }
202
203 result
204 }
205
206 #[cfg(not(target_arch = "wasm32"))]
211 #[inline(always)]
212 fn hash_subtree_parallel(&self, data: &[u8], length: usize) -> B256 {
213 debug_assert!(length.is_power_of_two());
214 debug_assert!(length >= SEGMENT_PAIR_LENGTH);
215
216 if length < BODY_SIZE {
218 return self.hash_subtree_sequential(data, length);
219 }
220
221 Self::hash_subtree_recursive_parallel_inner(data, length, self.cursor)
224 }
225
226 #[cfg(not(target_arch = "wasm32"))]
230 #[inline(always)]
231 fn hash_subtree_recursive_parallel_inner(data: &[u8], length: usize, cursor: usize) -> B256 {
232 debug_assert!(length.is_power_of_two());
233 debug_assert!(length >= SEGMENT_PAIR_LENGTH);
234
235 if length == SEGMENT_PAIR_LENGTH {
237 let mut hasher = Keccak256::new();
238 hasher.update(data);
239 return B256::from_slice(hasher.finalize().as_slice());
240 }
241
242 let half = length / 2;
243 let (left, right) = data.split_at(half);
244
245 let (left_hash, right_hash) = if half >= cursor {
248 let left_hash = Self::hash_subtree_recursive_parallel_inner(left, half, cursor);
250 let right_hash = ZERO_HASHES[Self::zero_tree_level(half)];
251 (left_hash, right_hash)
252 } else {
253 rayon::join(
257 || Self::hash_subtree_recursive_parallel_inner(left, half, half),
258 || Self::hash_subtree_recursive_parallel_inner(right, half, cursor - half),
259 )
260 };
261
262 let mut hasher = Keccak256::new();
263 hasher.update(left_hash.as_slice());
264 hasher.update(right_hash.as_slice());
265 B256::from_slice(hasher.finalize().as_slice())
266 }
267
268 #[inline(always)]
270 fn hash_subtree_sequential(&self, data: &[u8], length: usize) -> B256 {
271 debug_assert!(length.is_power_of_two());
272 debug_assert!(length >= SEGMENT_PAIR_LENGTH);
273
274 if length == SEGMENT_PAIR_LENGTH {
275 let mut hasher = Keccak256::new();
276 hasher.update(data);
277 return B256::from_slice(hasher.finalize().as_slice());
278 }
279
280 let half = length / 2;
281 let (left, right) = data.split_at(half);
282
283 let (left_hash, right_hash) = if half >= self.cursor {
285 let left_hash = self.hash_subtree_sequential(left, half);
287 let right_hash = ZERO_HASHES[Self::zero_tree_level(half)];
288 (left_hash, right_hash)
289 } else {
290 let left_hash = self.hash_subtree_sequential(left, half);
291 let right_hash = self.hash_subtree_sequential(right, half);
292 (left_hash, right_hash)
293 };
294
295 let mut hasher = Keccak256::new();
296 hasher.update(left_hash.as_slice());
297 hasher.update(right_hash.as_slice());
298 B256::from_slice(hasher.finalize().as_slice())
299 }
300
301 #[inline(always)]
304 const fn zero_tree_level(length: usize) -> usize {
305 length.trailing_zeros() as usize - 6
307 }
308
309 #[inline(always)]
311 fn finalize_with_prefix(&self, intermediate_hash: B256) -> B256 {
312 let mut hasher = Keccak256::new();
313
314 if let Some(prefix) = &self.prefix {
316 hasher.update(prefix);
317 }
318
319 hasher.update(self.span.to_le_bytes());
321
322 hasher.update(intermediate_hash.as_slice());
324
325 B256::from_slice(hasher.finalize().as_slice())
327 }
328
329 #[inline(always)]
331 const fn reset_internal(&mut self) {
332 self.cursor = 0;
334 self.span = 0;
335 }
337
338 #[inline]
340 #[must_use]
341 pub fn data(&self) -> Bytes {
342 if self.cursor == 0 {
343 return Bytes::new();
344 }
345
346 Bytes::copy_from_slice(&self.buffer[..self.cursor])
348 }
349
350 #[inline]
352 pub fn get_level_segments(&self, data: &[u8]) -> Vec<B256> {
353 let branches = branches_for_body_size(BODY_SIZE);
354
355 #[cfg(not(target_arch = "wasm32"))]
356 {
357 use rayon::prelude::*;
358 (0..branches)
359 .into_par_iter()
360 .map(|i| self.compute_segment_hash(data, i))
361 .collect()
362 }
363
364 #[cfg(target_arch = "wasm32")]
365 {
366 (0..branches)
367 .map(|i| self.compute_segment_hash(data, i))
368 .collect()
369 }
370 }
371
372 #[inline(always)]
374 fn compute_segment_hash(&self, data: &[u8], i: usize) -> B256 {
375 let start = i << SEGMENT_SIZE_LOG2; let mut hasher = Keccak256::new();
377
378 if start < data.len() {
379 let end = (start + SEGMENT_SIZE).min(data.len());
380 let segment_data = &data[start..end];
381
382 hasher.update(segment_data);
384
385 if segment_data.len() < SEGMENT_SIZE {
387 hasher.update(&[0u8; SEGMENT_SIZE][..(SEGMENT_SIZE - segment_data.len())]);
388 }
389 } else {
390 hasher.update([0u8; SEGMENT_SIZE]);
392 }
393
394 B256::from_slice(hasher.finalize().as_slice())
395 }
396}
397
398impl<const BODY_SIZE: usize> Write for Hasher<BODY_SIZE> {
399 #[inline]
400 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
401 let available = BODY_SIZE - self.cursor;
402 let to_write = buf.len().min(available);
403 if to_write > 0 {
404 self.buffer[self.cursor..self.cursor + to_write].copy_from_slice(&buf[..to_write]);
405 self.cursor += to_write;
406 }
407 Ok(to_write)
408 }
409
410 #[inline]
411 fn flush(&mut self) -> io::Result<()> {
412 Ok(())
413 }
414}
415
416impl<const BODY_SIZE: usize> OutputSizeUser for Hasher<BODY_SIZE> {
417 type OutputSize = U32;
418}
419
420impl<const BODY_SIZE: usize> Update for Hasher<BODY_SIZE> {
421 #[inline]
422 fn update(&mut self, data: &[u8]) {
423 self.update(data);
424 }
425}
426
427impl<const BODY_SIZE: usize> Reset for Hasher<BODY_SIZE> {
428 #[inline]
429 fn reset(&mut self) {
430 self.reset_internal();
431 }
432}
433
434impl<const BODY_SIZE: usize> FixedOutput for Hasher<BODY_SIZE> {
435 #[inline]
436 fn finalize_into(self, out: &mut Array<u8, Self::OutputSize>) {
437 let b256 = self.sum();
438 out.copy_from_slice(b256.as_slice());
439 }
440}
441
442impl<const BODY_SIZE: usize> FixedOutputReset for Hasher<BODY_SIZE> {
443 #[inline]
444 fn finalize_into_reset(&mut self, out: &mut Array<u8, Self::OutputSize>) {
445 let b256 = self.sum();
446 out.copy_from_slice(b256.as_slice());
447 self.reset_internal();
448 }
449}
450
451impl<const BODY_SIZE: usize> digest::HashMarker for Hasher<BODY_SIZE> {}
452
453#[derive(Debug, Default, Clone)]
455pub struct HasherFactory<const BODY_SIZE: usize = DEFAULT_BODY_SIZE>;
456
457impl<const BODY_SIZE: usize> HasherFactory<BODY_SIZE> {
458 #[inline]
460 pub const fn new() -> Self {
461 Self
462 }
463
464 #[inline]
466 pub const fn create_hasher(&self) -> Hasher<BODY_SIZE> {
467 Hasher::new()
468 }
469}