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
13use super::constants::*;
14
15const ZERO_TREE_LEVELS: usize = zero_tree_levels(DEFAULT_BODY_SIZE);
17
18static ZERO_HASHES: LazyLock<[B256; ZERO_TREE_LEVELS]> = LazyLock::new(|| {
20 let mut hashes = [B256::ZERO; ZERO_TREE_LEVELS];
21
22 let mut hasher = Keccak256::new();
24 hasher.update([0u8; SEGMENT_PAIR_LENGTH]);
25 hashes[0] = B256::from_slice(hasher.finalize().as_slice());
26
27 for i in 1..ZERO_TREE_LEVELS {
29 let mut hasher = Keccak256::new();
30 hasher.update(hashes[i - 1].as_slice());
31 hasher.update(hashes[i - 1].as_slice());
32 hashes[i] = B256::from_slice(hasher.finalize().as_slice());
33 }
34
35 hashes
36});
37
38pub(super) fn hash_pairs(prefix: Option<&[u8]>, pairs: &[u8], out: &mut [[u8; 32]]) {
44 debug_assert_eq!(pairs.len(), out.len() * SEGMENT_PAIR_LENGTH);
45
46 let Some(p) = prefix else {
47 let inputs: Vec<&[u8]> = pairs.chunks_exact(SEGMENT_PAIR_LENGTH).collect();
48 return keccak_batch::keccak256_many_into(&inputs, out);
49 };
50
51 let entry = p.len() + SEGMENT_PAIR_LENGTH;
52 let mut scratch = vec![0u8; entry * out.len()];
53 for (slot, pair) in scratch
54 .chunks_exact_mut(entry)
55 .zip(pairs.chunks_exact(SEGMENT_PAIR_LENGTH))
56 {
57 slot[..p.len()].copy_from_slice(p);
58 slot[p.len()..].copy_from_slice(pair);
59 }
60 let inputs: Vec<&[u8]> = scratch.chunks_exact(entry).collect();
61 keccak_batch::keccak256_many_into(&inputs, out);
62}
63
64#[derive(Debug, Clone)]
66pub struct Hasher<const BODY_SIZE: usize = DEFAULT_BODY_SIZE> {
67 span: u64,
68 prefix: Option<Vec<u8>>,
69 buffer: [u8; BODY_SIZE],
70 cursor: usize,
71}
72
73impl<const BODY_SIZE: usize> Default for Hasher<BODY_SIZE> {
74 #[inline]
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80impl<const BODY_SIZE: usize> Hasher<BODY_SIZE> {
81 #[inline]
83 pub const fn new() -> Self {
84 Self {
85 span: 0,
86 prefix: None,
87 buffer: [0u8; BODY_SIZE],
88 cursor: 0,
89 }
90 }
91
92 #[inline]
94 pub const fn set_span(&mut self, span: u64) {
95 self.span = span;
96 }
97
98 #[inline(always)]
100 pub const fn span(&self) -> u64 {
101 self.span
102 }
103
104 #[inline]
112 pub fn prefix_with(&mut self, prefix: &[u8]) {
113 self.prefix = Some(prefix.to_vec());
114 }
115
116 #[inline]
122 pub fn with_prefix(prefix: &[u8]) -> Self {
123 let mut hasher = Self::new();
124 hasher.prefix_with(prefix);
125 hasher
126 }
127
128 #[inline(always)]
134 fn node_hasher(prefix: Option<&[u8]>) -> Keccak256 {
135 let mut hasher = Keccak256::new();
136 if let Some(p) = prefix {
137 hasher.update(p);
138 }
139 hasher
140 }
141
142 #[inline(always)]
144 pub fn prefix(&self) -> &[u8] {
145 self.prefix.as_deref().unwrap_or(&[])
146 }
147
148 #[inline(always)]
150 pub const fn position(&self) -> usize {
151 self.cursor
152 }
153
154 #[inline(always)]
156 pub const fn len(&self) -> usize {
157 self.cursor
158 }
159
160 #[inline(always)]
162 pub const fn is_empty(&self) -> bool {
163 self.cursor == 0
164 }
165
166 #[inline]
168 pub fn update(&mut self, data: &[u8]) {
169 if data.is_empty() {
170 return;
171 }
172
173 let available_space = BODY_SIZE - self.cursor;
175 let bytes_to_copy = data.len().min(available_space);
176
177 if bytes_to_copy > 0 {
178 self.buffer[self.cursor..self.cursor + bytes_to_copy]
180 .copy_from_slice(&data[..bytes_to_copy]);
181
182 self.cursor += bytes_to_copy;
184 }
185 }
186
187 #[allow(clippy::should_implement_trait)] #[inline]
190 pub fn hash(&self, out: &mut [u8]) {
191 let hash = self.sum();
192 out.copy_from_slice(hash.as_slice());
193 }
194
195 #[inline]
197 #[must_use]
198 pub fn sum(&self) -> B256 {
199 self.finalize_with_prefix(self.hash_internal())
200 }
201
202 #[inline(always)]
205 fn is_all_zeros(data: &[u8]) -> bool {
206 data.iter().fold(0u8, |acc, &b| acc | b) == 0
209 }
210
211 #[inline(always)]
218 fn hash_internal(&self) -> B256 {
219 let prefix = self.prefix.as_deref();
220
221 let zero_hashes = self.zero_hashes(prefix);
226
227 if self.cursor == 0 {
229 return zero_hashes[ZERO_TREE_LEVELS - 1];
230 }
231
232 if Self::is_all_zeros(&self.buffer[..self.cursor]) {
236 return zero_hashes[ZERO_TREE_LEVELS - 1];
237 }
238
239 let effective_size = self
241 .cursor
242 .next_power_of_two()
243 .max(SEGMENT_PAIR_LENGTH)
244 .min(BODY_SIZE);
245
246 let mut result = self.hash_subtree(&self.buffer[..effective_size], &zero_hashes);
248
249 let mut current_size = effective_size;
251 while current_size < BODY_SIZE {
252 let sibling_level = Self::zero_tree_level(current_size);
254 let mut hasher = Self::node_hasher(prefix);
255 hasher.update(result.as_slice());
256 hasher.update(zero_hashes[sibling_level].as_slice());
257 result = B256::from_slice(hasher.finalize().as_slice());
258 current_size *= 2;
259 }
260
261 result
262 }
263
264 #[inline(always)]
272 fn zero_hashes(&self, prefix: Option<&[u8]>) -> [B256; ZERO_TREE_LEVELS] {
273 let Some(p) = prefix else {
274 return *ZERO_HASHES;
275 };
276
277 let mut hashes = [B256::ZERO; ZERO_TREE_LEVELS];
278
279 let mut hasher = Self::node_hasher(Some(p));
280 hasher.update([0u8; SEGMENT_PAIR_LENGTH]);
281 hashes[0] = B256::from_slice(hasher.finalize().as_slice());
282
283 for i in 1..ZERO_TREE_LEVELS {
284 let mut hasher = Self::node_hasher(Some(p));
285 hasher.update(hashes[i - 1].as_slice());
286 hasher.update(hashes[i - 1].as_slice());
287 hashes[i] = B256::from_slice(hasher.finalize().as_slice());
288 }
289
290 hashes
291 }
292
293 fn hash_subtree(&self, data: &[u8], zero_hashes: &[B256; ZERO_TREE_LEVELS]) -> B256 {
300 debug_assert!(data.len().is_power_of_two());
301 debug_assert!(data.len() >= SEGMENT_PAIR_LENGTH);
302
303 let prefix = self.prefix.as_deref();
304
305 if data.len() == SEGMENT_PAIR_LENGTH {
306 let mut hasher = Self::node_hasher(prefix);
307 hasher.update(data);
308 return B256::from_slice(hasher.finalize().as_slice());
309 }
310
311 let pairs = data.len() / SEGMENT_PAIR_LENGTH;
314 let mut live = self.cursor.div_ceil(SEGMENT_PAIR_LENGTH).min(pairs);
315 let mut level = vec![[0u8; 32]; pairs];
316 hash_pairs(
317 prefix,
318 &data[..live * SEGMENT_PAIR_LENGTH],
319 &mut level[..live],
320 );
321 for slot in &mut level[live..] {
322 slot.copy_from_slice(zero_hashes[0].as_slice());
323 }
324
325 let mut next = vec![[0u8; 32]; pairs / 2];
327 let mut count = pairs;
328 let mut depth = 1;
329 while count > 1 {
330 count /= 2;
331 live = live.div_ceil(2);
332 hash_pairs(prefix, level[..live * 2].as_flattened(), &mut next[..live]);
333 for slot in &mut next[live..count] {
334 slot.copy_from_slice(zero_hashes[depth].as_slice());
335 }
336 std::mem::swap(&mut level, &mut next);
337 depth += 1;
338 }
339
340 B256::from(level[0])
341 }
342
343 #[inline(always)]
346 const fn zero_tree_level(length: usize) -> usize {
347 length.trailing_zeros() as usize - 6
349 }
350
351 #[inline(always)]
353 fn finalize_with_prefix(&self, intermediate_hash: B256) -> B256 {
354 let mut hasher = Keccak256::new();
355
356 if let Some(prefix) = &self.prefix {
358 hasher.update(prefix);
359 }
360
361 hasher.update(self.span.to_le_bytes());
363
364 hasher.update(intermediate_hash.as_slice());
366
367 B256::from_slice(hasher.finalize().as_slice())
369 }
370
371 #[inline(always)]
373 const fn reset_internal(&mut self) {
374 self.cursor = 0;
376 self.span = 0;
377 }
379
380 #[inline]
382 #[must_use]
383 pub fn data(&self) -> Bytes {
384 if self.cursor == 0 {
385 return Bytes::new();
386 }
387
388 Bytes::copy_from_slice(&self.buffer[..self.cursor])
390 }
391
392 #[inline]
394 pub fn get_level_segments(&self, data: &[u8]) -> Vec<B256> {
395 let branches = branches_for_body_size(BODY_SIZE);
396
397 #[cfg(not(target_arch = "wasm32"))]
398 {
399 use rayon::prelude::*;
400 (0..branches)
401 .into_par_iter()
402 .map(|i| self.compute_segment_hash(data, i))
403 .collect()
404 }
405
406 #[cfg(target_arch = "wasm32")]
407 {
408 (0..branches)
409 .map(|i| self.compute_segment_hash(data, i))
410 .collect()
411 }
412 }
413
414 #[inline(always)]
416 fn compute_segment_hash(&self, data: &[u8], i: usize) -> B256 {
417 let start = i << SEGMENT_SIZE_LOG2; let mut hasher = Self::node_hasher(self.prefix.as_deref());
419
420 if start < data.len() {
421 let end = (start + SEGMENT_SIZE).min(data.len());
422 let segment_data = &data[start..end];
423
424 hasher.update(segment_data);
426
427 if segment_data.len() < SEGMENT_SIZE {
429 hasher.update(&[0u8; SEGMENT_SIZE][..(SEGMENT_SIZE - segment_data.len())]);
430 }
431 } else {
432 hasher.update([0u8; SEGMENT_SIZE]);
434 }
435
436 B256::from_slice(hasher.finalize().as_slice())
437 }
438}
439
440impl<const BODY_SIZE: usize> Write for Hasher<BODY_SIZE> {
441 #[inline]
442 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
443 let available = BODY_SIZE - self.cursor;
444 let to_write = buf.len().min(available);
445 if to_write > 0 {
446 self.buffer[self.cursor..self.cursor + to_write].copy_from_slice(&buf[..to_write]);
447 self.cursor += to_write;
448 }
449 Ok(to_write)
450 }
451
452 #[inline]
453 fn flush(&mut self) -> io::Result<()> {
454 Ok(())
455 }
456}
457
458impl<const BODY_SIZE: usize> OutputSizeUser for Hasher<BODY_SIZE> {
459 type OutputSize = U32;
460}
461
462impl<const BODY_SIZE: usize> Update for Hasher<BODY_SIZE> {
463 #[inline]
464 fn update(&mut self, data: &[u8]) {
465 self.update(data);
466 }
467}
468
469impl<const BODY_SIZE: usize> Reset for Hasher<BODY_SIZE> {
470 #[inline]
471 fn reset(&mut self) {
472 self.reset_internal();
473 }
474}
475
476impl<const BODY_SIZE: usize> FixedOutput for Hasher<BODY_SIZE> {
477 #[inline]
478 fn finalize_into(self, out: &mut Array<u8, Self::OutputSize>) {
479 let b256 = self.sum();
480 out.copy_from_slice(b256.as_slice());
481 }
482}
483
484impl<const BODY_SIZE: usize> FixedOutputReset for Hasher<BODY_SIZE> {
485 #[inline]
486 fn finalize_into_reset(&mut self, out: &mut Array<u8, Self::OutputSize>) {
487 let b256 = self.sum();
488 out.copy_from_slice(b256.as_slice());
489 self.reset_internal();
490 }
491}
492
493impl<const BODY_SIZE: usize> digest::HashMarker for Hasher<BODY_SIZE> {}
494
495#[derive(Debug, Default, Clone)]
497pub struct HasherFactory<const BODY_SIZE: usize = DEFAULT_BODY_SIZE>;
498
499impl<const BODY_SIZE: usize> HasherFactory<BODY_SIZE> {
500 #[inline]
502 pub const fn new() -> Self {
503 Self
504 }
505
506 #[inline]
508 pub const fn create_hasher(&self) -> Hasher<BODY_SIZE> {
509 Hasher::new()
510 }
511}