Skip to main content

binius_hash/
blake3.rs

1// Copyright 2026 The Binius Developers
2
3//! Blake3 leaf hash and two-to-one compression for Merkle tree constructions.
4//!
5//! The compression is a plain Blake3 hash of the two children concatenated.
6//! The batched kernels a prover wants live in the prover-side crate, not here.
7
8use digest::Output;
9
10use crate::{compress::CompressionFunction, suite::HashSuite};
11
12/// A two-to-one compression function that hashes the concatenation of its inputs with Blake3.
13#[derive(Debug, Clone, Default)]
14pub struct Blake3Compression;
15
16impl CompressionFunction<Output<blake3::Hasher>, 2> for Blake3Compression {
17	fn compress(&self, input: [Output<blake3::Hasher>; 2]) -> Output<blake3::Hasher> {
18		let mut hasher = blake3::Hasher::new();
19		hasher.update(input[0].as_slice());
20		hasher.update(input[1].as_slice());
21		(*hasher.finalize().as_bytes()).into()
22	}
23}
24
25/// Blake3 leaves and a Blake3 compression function for inner nodes.
26#[derive(Debug, Clone, Default)]
27pub struct Blake3HashSuite;
28
29impl HashSuite for Blake3HashSuite {
30	type LeafHash = blake3::Hasher;
31	type Compression = Blake3Compression;
32}
33
34#[cfg(test)]
35mod tests {
36	use rand::{RngExt, SeedableRng, rngs::StdRng};
37
38	use super::*;
39
40	#[test]
41	fn test_compression_matches_reference_hash() {
42		let mut rng = StdRng::seed_from_u64(0);
43		let left: [u8; 32] = rng.random();
44		let right: [u8; 32] = rng.random();
45
46		let compressed = Blake3Compression.compress([left.into(), right.into()]);
47
48		// Invariant: the compression is the reference hash of the two children concatenated.
49		let mut concatenated = [0u8; 64];
50		concatenated[..32].copy_from_slice(&left);
51		concatenated[32..].copy_from_slice(&right);
52		assert_eq!(compressed.as_slice(), blake3::hash(&concatenated).as_bytes());
53	}
54}