1use std::{fmt::Debug, mem::MaybeUninit};
5
6use binius_field::Field;
7use binius_utils::{
8 checked_arithmetics::log2_strict_usize,
9 rand::par_rand,
10 rayon::{prelude::*, slice::ParallelSlice},
11};
12use digest::{Digest, FixedOutputReset, Output, block_api::BlockSizeUser};
13use rand::{CryptoRng, Rng, rngs::StdRng};
14
15use super::{
16 compress::CompressionFunction, parallel_compression::ParallelPseudoCompression,
17 parallel_digest::ParallelDigest,
18};
19
20pub trait HashSuite {
28 type LeafHash: Digest + BlockSizeUser + FixedOutputReset + Send;
30 type Compression: CompressionFunction<Output<Self::LeafHash>, 2> + Default;
32 type ParLeafHash: ParallelDigest<Digest = Self::LeafHash> + Default;
34 type ParCompression: ParallelPseudoCompression<Output<Self::LeafHash>, 2, Compression = Self::Compression>
36 + Default;
37}
38
39#[derive(Debug, thiserror::Error)]
40pub enum Error {
41 #[error("Index exceeds Merkle tree base size: {max}")]
42 IndexOutOfRange { max: usize },
43 #[error("values length must be a multiple of the batch size")]
44 IncorrectBatchSize,
45 #[error("The argument length must be a power of two.")]
46 PowerOfTwoLengthRequired,
47 #[error("The layer does not exist in the Merkle tree")]
48 IncorrectLayerDepth,
49}
50
51#[derive(Debug, Clone)]
57pub struct BinaryMerkleTree<D, F> {
58 pub log_len: usize,
60 pub inner_nodes: Vec<D>,
62 pub salts: Vec<F>,
64}
65
66pub fn build<F, H, R>(
67 elements: &[F],
68 batch_size: usize,
69 salt_len: usize,
70 rng: R,
71) -> Result<BinaryMerkleTree<Output<H::LeafHash>, F>, Error>
72where
73 F: Field,
74 H: HashSuite,
75 R: Rng + CryptoRng,
76{
77 if !elements.len().is_multiple_of(batch_size) {
78 return Err(Error::IncorrectBatchSize);
79 }
80
81 let len = elements.len() / batch_size;
82
83 if !len.is_power_of_two() {
84 return Err(Error::PowerOfTwoLengthRequired);
85 }
86
87 build_from_iterator::<_, H, _, _>(
88 elements
89 .par_chunks(batch_size)
90 .map(|chunk| chunk.iter().copied()),
91 batch_size,
92 salt_len,
93 rng,
94 )
95}
96
97pub fn build_from_iterator<F, H, R, ParIter>(
98 iterated_chunks: ParIter,
99 n_items_per_input: usize,
100 salt_len: usize,
101 mut rng: R,
102) -> Result<BinaryMerkleTree<Output<H::LeafHash>, F>, Error>
103where
104 F: Field,
105 H: HashSuite,
106 R: Rng + CryptoRng,
107 ParIter: IndexedParallelIterator<Item: IntoIterator<Item = F, IntoIter: Send>>,
108{
109 let log_len = log2_strict_usize(iterated_chunks.len()); let salts =
113 par_rand::<StdRng, _, _>(salt_len << log_len, &mut rng, F::random).collect::<Vec<_>>();
114
115 let total_length = (1 << (log_len + 1)) - 1;
116 let mut inner_nodes = Vec::with_capacity(total_length);
117 hash_leaves::<F, H, _>(
118 iterated_chunks,
119 n_items_per_input,
120 &mut inner_nodes.spare_capacity_mut()[..(1 << log_len)],
121 &salts,
122 );
123
124 let (prev_layer, mut remaining) = inner_nodes.spare_capacity_mut().split_at_mut(1 << log_len);
125
126 let mut prev_layer = unsafe {
127 prev_layer.assume_init_mut()
129 };
130 let parallel_compression = H::ParCompression::default();
131 for i in 1..(log_len + 1) {
132 let (next_layer, next_remaining) = remaining.split_at_mut(1 << (log_len - i));
133 remaining = next_remaining;
134
135 parallel_compression.parallel_compress(prev_layer, next_layer);
136
137 prev_layer = unsafe {
138 next_layer.assume_init_mut()
140 };
141 }
142
143 unsafe {
144 inner_nodes.set_len(total_length);
148 }
149 Ok(BinaryMerkleTree {
150 log_len,
151 inner_nodes,
152 salts,
153 })
154}
155
156impl<D: Clone, F> BinaryMerkleTree<D, F> {
157 pub fn root(&self) -> D {
158 self.inner_nodes
159 .last()
160 .expect("MerkleTree inner nodes can't be empty")
161 .clone()
162 }
163
164 pub fn get_salt(&self, index: usize) -> &[F] {
169 assert!(index < (1 << self.log_len));
170 let salt_len = self.salts.len() >> self.log_len;
171 &self.salts[index * salt_len..(index + 1) * salt_len]
172 }
173
174 pub fn layer(&self, layer_depth: usize) -> Result<&[D], Error> {
175 if layer_depth > self.log_len {
176 return Err(Error::IncorrectLayerDepth);
177 }
178 let range_start = self.inner_nodes.len() + 1 - (1 << (layer_depth + 1));
179
180 Ok(&self.inner_nodes[range_start..range_start + (1 << layer_depth)])
181 }
182
183 pub fn branch(&self, index: usize, layer_depth: usize) -> Result<Vec<D>, Error> {
187 if index >= 1 << self.log_len || layer_depth > self.log_len {
188 return Err(Error::IndexOutOfRange {
189 max: (1 << self.log_len) - 1,
190 });
191 }
192
193 let branch = (0..self.log_len - layer_depth)
194 .map(|j| {
195 let node_index = (((1 << j) - 1) << (self.log_len + 1 - j)) | (index >> j) ^ 1;
196 self.inner_nodes[node_index].clone()
197 })
198 .collect();
199
200 Ok(branch)
201 }
202}
203
204#[tracing::instrument("hash_leaves", skip_all, level = "debug")]
216fn hash_leaves<F, H, ParIter>(
217 iterated_chunks: ParIter,
218 n_items_per_input: usize,
219 digests: &mut [MaybeUninit<Output<H::LeafHash>>],
220 salts: &[F],
221) where
222 F: Field,
223 H: HashSuite,
224 ParIter: IndexedParallelIterator<Item: IntoIterator<Item = F, IntoIter: Send>>,
225{
226 if salts.is_empty() {
227 let hasher = H::ParLeafHash::default();
230 hasher.digest_with_const_len(n_items_per_input, iterated_chunks, digests);
231 } else {
232 assert!(salts.len().is_multiple_of(digests.len()));
233
234 let salt_len = salts.len() / digests.len();
235
236 let salted_iter = iterated_chunks
238 .zip(salts.par_chunks(salt_len))
239 .map(|(chunk, salt)| chunk.into_iter().chain(salt.iter().copied()));
240
241 let hasher = H::ParLeafHash::default();
243 hasher.digest_with_const_len(n_items_per_input + salt_len, salted_iter, digests);
244 }
245}