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]
90 pub fn prefix_with(&mut self, prefix: &[u8]) {
91 self.prefix = Some(prefix.to_vec());
92 }
93
94 #[inline]
100 pub fn with_prefix(prefix: &[u8]) -> Self {
101 let mut hasher = Self::new();
102 hasher.prefix_with(prefix);
103 hasher
104 }
105
106 #[inline(always)]
112 fn node_hasher(prefix: Option<&[u8]>) -> Keccak256 {
113 let mut hasher = Keccak256::new();
114 if let Some(p) = prefix {
115 hasher.update(p);
116 }
117 hasher
118 }
119
120 #[inline(always)]
122 pub fn prefix(&self) -> &[u8] {
123 self.prefix.as_deref().unwrap_or(&[])
124 }
125
126 #[inline(always)]
128 pub const fn position(&self) -> usize {
129 self.cursor
130 }
131
132 #[inline(always)]
134 pub const fn len(&self) -> usize {
135 self.cursor
136 }
137
138 #[inline(always)]
140 pub const fn is_empty(&self) -> bool {
141 self.cursor == 0
142 }
143
144 #[inline]
146 pub fn update(&mut self, data: &[u8]) {
147 if data.is_empty() {
148 return;
149 }
150
151 let available_space = BODY_SIZE - self.cursor;
153 let bytes_to_copy = data.len().min(available_space);
154
155 if bytes_to_copy > 0 {
156 self.buffer[self.cursor..self.cursor + bytes_to_copy]
158 .copy_from_slice(&data[..bytes_to_copy]);
159
160 self.cursor += bytes_to_copy;
162 }
163 }
164
165 #[allow(clippy::should_implement_trait)] #[inline]
168 pub fn hash(&self, out: &mut [u8]) {
169 let hash = self.sum();
170 out.copy_from_slice(hash.as_slice());
171 }
172
173 #[inline]
175 #[must_use]
176 pub fn sum(&self) -> B256 {
177 self.finalize_with_prefix(self.hash_internal())
178 }
179
180 #[inline(always)]
183 fn is_all_zeros(data: &[u8]) -> bool {
184 data.iter().fold(0u8, |acc, &b| acc | b) == 0
187 }
188
189 #[inline(always)]
196 fn hash_internal(&self) -> B256 {
197 let prefix = self.prefix.as_deref();
198
199 let zero_hashes = self.zero_hashes(prefix);
204
205 if self.cursor == 0 {
207 return zero_hashes[ZERO_TREE_LEVELS - 1];
208 }
209
210 if Self::is_all_zeros(&self.buffer[..self.cursor]) {
214 return zero_hashes[ZERO_TREE_LEVELS - 1];
215 }
216
217 let effective_size = self
219 .cursor
220 .next_power_of_two()
221 .max(SEGMENT_PAIR_LENGTH)
222 .min(BODY_SIZE);
223
224 #[cfg(not(target_arch = "wasm32"))]
229 let mut result = if prefix.is_some() {
230 self.hash_subtree_sequential(&self.buffer[..effective_size], effective_size)
231 } else {
232 self.hash_subtree_parallel(&self.buffer[..effective_size], effective_size)
233 };
234
235 #[cfg(target_arch = "wasm32")]
236 let mut result =
237 self.hash_subtree_sequential(&self.buffer[..effective_size], effective_size);
238
239 let mut current_size = effective_size;
241 while current_size < BODY_SIZE {
242 let sibling_level = Self::zero_tree_level(current_size);
244 let mut hasher = Self::node_hasher(prefix);
245 hasher.update(result.as_slice());
246 hasher.update(zero_hashes[sibling_level].as_slice());
247 result = B256::from_slice(hasher.finalize().as_slice());
248 current_size *= 2;
249 }
250
251 result
252 }
253
254 #[inline(always)]
262 fn zero_hashes(&self, prefix: Option<&[u8]>) -> [B256; ZERO_TREE_LEVELS] {
263 let Some(p) = prefix else {
264 return *ZERO_HASHES;
265 };
266
267 let mut hashes = [B256::ZERO; ZERO_TREE_LEVELS];
268
269 let mut hasher = Self::node_hasher(Some(p));
270 hasher.update([0u8; SEGMENT_PAIR_LENGTH]);
271 hashes[0] = B256::from_slice(hasher.finalize().as_slice());
272
273 for i in 1..ZERO_TREE_LEVELS {
274 let mut hasher = Self::node_hasher(Some(p));
275 hasher.update(hashes[i - 1].as_slice());
276 hasher.update(hashes[i - 1].as_slice());
277 hashes[i] = B256::from_slice(hasher.finalize().as_slice());
278 }
279
280 hashes
281 }
282
283 #[cfg(not(target_arch = "wasm32"))]
288 #[inline(always)]
289 fn hash_subtree_parallel(&self, data: &[u8], length: usize) -> B256 {
290 debug_assert!(length.is_power_of_two());
291 debug_assert!(length >= SEGMENT_PAIR_LENGTH);
292
293 if length < BODY_SIZE {
295 return self.hash_subtree_sequential(data, length);
296 }
297
298 Self::hash_subtree_recursive_parallel_inner(data, length, self.cursor)
301 }
302
303 #[cfg(not(target_arch = "wasm32"))]
307 #[inline(always)]
308 fn hash_subtree_recursive_parallel_inner(data: &[u8], length: usize, cursor: usize) -> B256 {
309 debug_assert!(length.is_power_of_two());
310 debug_assert!(length >= SEGMENT_PAIR_LENGTH);
311
312 if length == SEGMENT_PAIR_LENGTH {
314 let mut hasher = Keccak256::new();
315 hasher.update(data);
316 return B256::from_slice(hasher.finalize().as_slice());
317 }
318
319 let half = length / 2;
320 let (left, right) = data.split_at(half);
321
322 let (left_hash, right_hash) = if half >= cursor {
325 let left_hash = Self::hash_subtree_recursive_parallel_inner(left, half, cursor);
327 let right_hash = ZERO_HASHES[Self::zero_tree_level(half)];
328 (left_hash, right_hash)
329 } else {
330 rayon::join(
334 || Self::hash_subtree_recursive_parallel_inner(left, half, half),
335 || Self::hash_subtree_recursive_parallel_inner(right, half, cursor - half),
336 )
337 };
338
339 let mut hasher = Keccak256::new();
340 hasher.update(left_hash.as_slice());
341 hasher.update(right_hash.as_slice());
342 B256::from_slice(hasher.finalize().as_slice())
343 }
344
345 #[inline(always)]
347 fn hash_subtree_sequential(&self, data: &[u8], length: usize) -> B256 {
348 debug_assert!(length.is_power_of_two());
349 debug_assert!(length >= SEGMENT_PAIR_LENGTH);
350
351 let prefix = self.prefix.as_deref();
352
353 if length == SEGMENT_PAIR_LENGTH {
354 let mut hasher = Self::node_hasher(prefix);
355 hasher.update(data);
356 return B256::from_slice(hasher.finalize().as_slice());
357 }
358
359 let half = length / 2;
360 let (left, right) = data.split_at(half);
361
362 let (left_hash, right_hash) = if half >= self.cursor && prefix.is_none() {
367 let left_hash = self.hash_subtree_sequential(left, half);
369 let right_hash = ZERO_HASHES[Self::zero_tree_level(half)];
370 (left_hash, right_hash)
371 } else {
372 let left_hash = self.hash_subtree_sequential(left, half);
373 let right_hash = self.hash_subtree_sequential(right, half);
374 (left_hash, right_hash)
375 };
376
377 let mut hasher = Self::node_hasher(prefix);
378 hasher.update(left_hash.as_slice());
379 hasher.update(right_hash.as_slice());
380 B256::from_slice(hasher.finalize().as_slice())
381 }
382
383 #[inline(always)]
386 const fn zero_tree_level(length: usize) -> usize {
387 length.trailing_zeros() as usize - 6
389 }
390
391 #[inline(always)]
393 fn finalize_with_prefix(&self, intermediate_hash: B256) -> B256 {
394 let mut hasher = Keccak256::new();
395
396 if let Some(prefix) = &self.prefix {
398 hasher.update(prefix);
399 }
400
401 hasher.update(self.span.to_le_bytes());
403
404 hasher.update(intermediate_hash.as_slice());
406
407 B256::from_slice(hasher.finalize().as_slice())
409 }
410
411 #[inline(always)]
413 const fn reset_internal(&mut self) {
414 self.cursor = 0;
416 self.span = 0;
417 }
419
420 #[inline]
422 #[must_use]
423 pub fn data(&self) -> Bytes {
424 if self.cursor == 0 {
425 return Bytes::new();
426 }
427
428 Bytes::copy_from_slice(&self.buffer[..self.cursor])
430 }
431
432 #[inline]
434 pub fn get_level_segments(&self, data: &[u8]) -> Vec<B256> {
435 let branches = branches_for_body_size(BODY_SIZE);
436
437 #[cfg(not(target_arch = "wasm32"))]
438 {
439 use rayon::prelude::*;
440 (0..branches)
441 .into_par_iter()
442 .map(|i| self.compute_segment_hash(data, i))
443 .collect()
444 }
445
446 #[cfg(target_arch = "wasm32")]
447 {
448 (0..branches)
449 .map(|i| self.compute_segment_hash(data, i))
450 .collect()
451 }
452 }
453
454 #[inline(always)]
456 fn compute_segment_hash(&self, data: &[u8], i: usize) -> B256 {
457 let start = i << SEGMENT_SIZE_LOG2; let mut hasher = Self::node_hasher(self.prefix.as_deref());
459
460 if start < data.len() {
461 let end = (start + SEGMENT_SIZE).min(data.len());
462 let segment_data = &data[start..end];
463
464 hasher.update(segment_data);
466
467 if segment_data.len() < SEGMENT_SIZE {
469 hasher.update(&[0u8; SEGMENT_SIZE][..(SEGMENT_SIZE - segment_data.len())]);
470 }
471 } else {
472 hasher.update([0u8; SEGMENT_SIZE]);
474 }
475
476 B256::from_slice(hasher.finalize().as_slice())
477 }
478}
479
480impl<const BODY_SIZE: usize> Write for Hasher<BODY_SIZE> {
481 #[inline]
482 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
483 let available = BODY_SIZE - self.cursor;
484 let to_write = buf.len().min(available);
485 if to_write > 0 {
486 self.buffer[self.cursor..self.cursor + to_write].copy_from_slice(&buf[..to_write]);
487 self.cursor += to_write;
488 }
489 Ok(to_write)
490 }
491
492 #[inline]
493 fn flush(&mut self) -> io::Result<()> {
494 Ok(())
495 }
496}
497
498impl<const BODY_SIZE: usize> OutputSizeUser for Hasher<BODY_SIZE> {
499 type OutputSize = U32;
500}
501
502impl<const BODY_SIZE: usize> Update for Hasher<BODY_SIZE> {
503 #[inline]
504 fn update(&mut self, data: &[u8]) {
505 self.update(data);
506 }
507}
508
509impl<const BODY_SIZE: usize> Reset for Hasher<BODY_SIZE> {
510 #[inline]
511 fn reset(&mut self) {
512 self.reset_internal();
513 }
514}
515
516impl<const BODY_SIZE: usize> FixedOutput for Hasher<BODY_SIZE> {
517 #[inline]
518 fn finalize_into(self, out: &mut Array<u8, Self::OutputSize>) {
519 let b256 = self.sum();
520 out.copy_from_slice(b256.as_slice());
521 }
522}
523
524impl<const BODY_SIZE: usize> FixedOutputReset for Hasher<BODY_SIZE> {
525 #[inline]
526 fn finalize_into_reset(&mut self, out: &mut Array<u8, Self::OutputSize>) {
527 let b256 = self.sum();
528 out.copy_from_slice(b256.as_slice());
529 self.reset_internal();
530 }
531}
532
533impl<const BODY_SIZE: usize> digest::HashMarker for Hasher<BODY_SIZE> {}
534
535#[derive(Debug, Default, Clone)]
537pub struct HasherFactory<const BODY_SIZE: usize = DEFAULT_BODY_SIZE>;
538
539impl<const BODY_SIZE: usize> HasherFactory<BODY_SIZE> {
540 #[inline]
542 pub const fn new() -> Self {
543 Self
544 }
545
546 #[inline]
548 pub const fn create_hasher(&self) -> Hasher<BODY_SIZE> {
549 Hasher::new()
550 }
551}