1use amplify::confinement::{LargeVec, MediumOrdMap};
27use amplify::num::{u256, u5};
28use amplify::Wrapper;
29
30pub use self::commit::Error;
31use crate::merkle::MerkleHash;
32use crate::mpc::atoms::Leaf;
33use crate::mpc::{
34 Commitment, MerkleBlock, MerkleConcealed, MerkleProof, Message, MessageMap, Method, Proof,
35 ProtocolId,
36};
37use crate::{CommitId, Conceal, LIB_NAME_COMMIT_VERIFY};
38
39#[allow(dead_code)]
41const COFACTOR_ATTEMPTS: u16 = 500;
42
43type OrderedMap = MediumOrdMap<u32, (ProtocolId, Message)>;
44
45#[derive(Clone, PartialEq, Eq, Hash, Debug)]
47#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
48#[strict_type(lib = LIB_NAME_COMMIT_VERIFY)]
49#[derive(CommitEncode)]
50#[commit_encode(crate = crate, strategy = conceal, id = Commitment)]
51pub struct MerkleTree {
52 pub(super) method: Method,
54
55 pub(super) depth: u5,
57
58 pub(super) entropy: u64,
60
61 pub(super) cofactor: u16,
64
65 pub(super) messages: MessageMap,
67
68 pub(super) map: OrderedMap,
69}
70
71impl Proof for MerkleTree {
72 fn matches(&self, other: &Self) -> bool { self.commit_id() == other.commit_id() }
73}
74
75impl MerkleTree {
76 pub fn root(&self) -> MerkleHash {
78 let iter = (0..self.width_limit()).map(|pos| {
79 self.map
80 .get(&pos)
81 .map(|(protocol, msg)| Leaf::inhabited(*protocol, *msg))
82 .unwrap_or_else(|| Leaf::entropy(self.entropy, pos))
83 });
84 let leaves = LargeVec::try_from_iter(iter).expect("tree width has u32-bound size");
85 debug_assert_eq!(leaves.len_u32(), self.width_limit());
86 MerkleHash::merklize(&leaves)
87 }
88}
89
90impl Conceal for MerkleTree {
91 type Concealed = MerkleConcealed;
92
93 fn conceal(&self) -> Self::Concealed { MerkleBlock::from(self.clone()).conceal() }
94}
95
96mod commit {
97 use std::collections::BTreeMap;
98
99 use amplify::confinement::Confined;
100
101 use super::*;
102 use crate::mpc::MultiSource;
103 use crate::{TryCommitVerify, UntaggedProtocol};
104
105 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Error, Debug, Display)]
108 #[display(doc_comments)]
109 pub enum Error {
110 Empty,
113
114 TooManyMessages(usize),
117
118 CantFitInMaxSlots(usize),
121 }
122
123 impl TryCommitVerify<MultiSource, UntaggedProtocol> for MerkleTree {
128 type Error = Error;
129
130 fn try_commit(source: &MultiSource) -> Result<Self, Error> {
131 #[cfg(feature = "rand")]
132 use rand::{rng, RngCore};
133
134 let msg_count = source.messages.len();
135
136 if source.min_depth == u5::ZERO && source.messages.is_empty() {
137 return Err(Error::Empty);
138 }
139 if msg_count > 2usize.pow(u5::MAX.to_u8() as u32) {
140 return Err(Error::TooManyMessages(msg_count));
141 }
142
143 #[cfg(feature = "rand")]
144 let entropy = source.static_entropy.unwrap_or_else(|| rng().next_u64());
145 #[cfg(not(feature = "rand"))]
146 let entropy = source.static_entropy.expect(
147 "use must use `rand` feature for crate commit_verify if you do not provide static \
148 entropy information in `MultiSource`",
149 );
150
151 let mut map = BTreeMap::<u32, (ProtocolId, Message)>::new();
152
153 let mut depth = source.min_depth;
154 let mut prev_width = 1u32;
155 loop {
156 let width_limit = 2u32.pow(depth.to_u8() as u32);
157 if width_limit as usize >= msg_count {
158 for cofactor in 0..=(prev_width.min(COFACTOR_ATTEMPTS as u32) as u16) {
159 map.clear();
160 if source.messages.iter().all(|(protocol, message)| {
161 let pos = protocol_id_pos(*protocol, cofactor, depth);
162 map.insert(pos, (*protocol, *message)).is_none()
163 }) {
164 return Ok(MerkleTree {
165 method: source.method,
166 depth,
167 entropy,
168 cofactor,
169 messages: source.messages.clone(),
170 map: Confined::try_from(map).expect("MultiSource type guarantees"),
171 });
172 }
173 }
174 }
175
176 prev_width = width_limit;
177 depth = depth
178 .checked_add(1)
179 .ok_or(Error::CantFitInMaxSlots(msg_count))?;
180 }
181 }
182 }
183}
184
185pub(super) fn protocol_id_pos(protocol_id: ProtocolId, cofactor: u16, depth: u5) -> u32 {
186 let width = 2u32.pow(depth.to_u8() as u32);
187 debug_assert_ne!(width, 0);
188 let rem = u256::from_le_bytes((*protocol_id).into_inner()) %
189 u256::from(width.saturating_sub(cofactor as u32).max(1) as u64);
190 rem.low_u64() as u32
191}
192
193impl MerkleTree {
194 pub fn protocol_id_pos(&self, protocol_id: ProtocolId) -> u32 {
196 protocol_id_pos(protocol_id, self.cofactor, self.depth)
197 }
198
199 pub fn width_limit(&self) -> u32 { 2u32.pow(self.depth.to_u8() as u32) }
202
203 pub fn factored_width(&self) -> u32 { self.width_limit() - self.cofactor as u32 }
206
207 pub fn depth(&self) -> u5 { self.depth }
209
210 pub fn cofactor(&self) -> u16 { self.cofactor }
212
213 pub fn entropy(&self) -> u64 { self.entropy }
215
216 pub fn into_proofs(self) -> impl Iterator<Item = (ProtocolId, MerkleProof)> {
219 let block = MerkleBlock::from(self);
220 block.into_known_proofs()
221 }
222}
223
224#[cfg(test)]
225pub(crate) mod test_helpers {
226 #![cfg_attr(coverage_nightly, coverage(off))]
227
228 use std::collections::BTreeMap;
229
230 use amplify::confinement::Confined;
231 use amplify::Bytes32;
232 use rand::random;
233
234 use super::*;
235 use crate::mpc::MultiSource;
236 use crate::TryCommitVerify;
237
238 pub fn make_det_messages(no: u32) -> BTreeMap<ProtocolId, Message> {
239 let mut msgs = BTreeMap::new();
240 for _ in 0..no {
241 let protocol_id = u256::from(no);
242 let msg = random::<u8>();
243 msgs.insert(
244 ProtocolId::from(protocol_id.to_le_bytes()),
245 Message::from_inner(Bytes32::with_fill(msg)),
246 );
247 }
248 msgs
249 }
250
251 pub fn make_random_messages(no: u32) -> BTreeMap<ProtocolId, Message> {
252 let mut msgs = BTreeMap::new();
253 for _ in 0..no {
254 let protocol_id = random::<u128>();
255 let protocol_id = u256::from(protocol_id);
256 let msg = random::<u8>();
257 msgs.insert(
258 ProtocolId::from(protocol_id.to_le_bytes()),
259 Message::from_inner(Bytes32::with_fill(msg)),
260 );
261 }
262 msgs
263 }
264
265 pub fn make_random_tree(msgs: &BTreeMap<ProtocolId, Message>) -> MerkleTree {
266 let src = MultiSource {
267 method: Method::Sha256t,
268 min_depth: u5::ZERO,
269 messages: Confined::try_from_iter(msgs.iter().map(|(a, b)| (*a, *b))).unwrap(),
270 static_entropy: None,
271 };
272 MerkleTree::try_commit(&src).unwrap()
273 }
274}
275
276#[cfg(test)]
277mod test {
278 #![cfg_attr(coverage_nightly, coverage(off))]
279
280 use std::collections::BTreeSet;
281
282 use amplify::num::u5;
283 use amplify::Wrapper;
284 use rand::random;
285 use strict_encoding::{StreamWriter, StrictEncode};
286
287 use crate::mpc::tree::test_helpers::{make_random_messages, make_random_tree};
288 use crate::mpc::MerkleBlock;
289 use crate::{CommitId, Conceal};
290
291 #[test]
292 #[should_panic(expected = "Empty")]
293 fn tree_empty() {
294 let msgs = make_random_messages(0);
295 make_random_tree(&msgs);
296 }
297
298 #[test]
299 fn tree_sizing() {
300 for size in 1..16 {
301 let msgs = make_random_messages(size);
302 make_random_tree(&msgs);
303 }
304 for exp in 5..=8 {
305 let size = 2u32.pow(exp);
306
307 let msgs = make_random_messages(size);
308 make_random_tree(&msgs);
309
310 let msgs = make_random_messages(size - 9);
311 make_random_tree(&msgs);
312
313 let msgs = make_random_messages(size + 13);
314 make_random_tree(&msgs);
315 }
316 }
317
318 #[test]
319 #[ignore = "should be executed alone for performance reasons"]
320 fn tree_huge() {
321 use std::time::Instant;
327
328 let count = 1_048_576 / 128;
329 let msgs = make_random_messages(count);
330
331 let start = Instant::now();
332 let tree = make_random_tree(&msgs);
333 let elapsed_gen = start.elapsed();
334
335 let mut counter = StreamWriter::counter::<{ usize::MAX }>();
336 tree.strict_write(&mut counter).unwrap();
337 eprintln!(
338 "Tree with {count} protocol-messages: depth {}, cofactor {}, width {}.\nSerialized \
339 length {} bytes.\nTakes {} msecs to generate",
340 tree.depth,
341 tree.cofactor,
342 tree.factored_width(),
343 counter.unconfine().count,
344 elapsed_gen.as_millis(),
345 );
346
347 let start = Instant::now();
348 let root = tree.root();
349 let elapsed_root = start.elapsed();
350 eprintln!("Root is {root}. Takes {} msecs to compute", elapsed_root.as_millis(),);
351 }
352
353 #[test]
354 fn tree_structure() {
355 let msgs = make_random_messages(9);
356 let tree = make_random_tree(&msgs);
357 assert!(tree.depth() > u5::with(3));
358 assert!(tree.factored_width() > 9);
359 let mut set = BTreeSet::<u32>::new();
360 for (pid, msg) in msgs {
361 let pos = tree.protocol_id_pos(pid);
362 assert!(set.insert(pos));
363 assert_eq!(tree.messages.get(&pid), Some(&msg));
364 }
365 }
366
367 #[test]
368 fn tree_conceal() {
369 let msgs = make_random_messages(9);
370 let tree = make_random_tree(&msgs);
371 assert_eq!(tree.conceal(), MerkleBlock::from(tree.clone()).conceal());
372 }
373
374 #[test]
375 fn tree_id() {
376 let msgs = make_random_messages(9);
377 let tree = make_random_tree(&msgs);
378 let id = tree.commit_id();
379 let root = tree.root();
380 assert_ne!(id.into_inner(), root.into_inner());
381 }
382
383 #[test]
384 fn tree_id_entropy() {
385 let msgs = make_random_messages(9);
386 let mut tree = make_random_tree(&msgs);
387 let id1 = tree.commit_id();
388
389 tree.entropy = loop {
390 let entropy = random();
391 if entropy != tree.entropy {
392 break entropy;
393 }
394 };
395 let id2 = tree.commit_id();
396
397 assert_ne!(id1, id2);
398 }
399
400 #[test]
401 #[ignore = "should be executed alone for performance reasons"]
402 fn scalability() {
403 let mut depths = vec![];
404 let mut cofacs = vec![];
405 for _ in 0..10 {
406 let msgs = make_random_messages(500);
407 let tree = make_random_tree(&msgs);
408 depths.push(tree.depth.to_u8());
409 cofacs.push(tree.cofactor);
410 }
411 let davg = depths.iter().map(|v| *v as u32).sum::<u32>() as f32 / 10f32;
412 eprintln!("Depth: avg={davg:.2} {depths:?}");
413 eprintln!("Cofactors: {cofacs:?}");
414 assert!(davg <= 15f32);
415 }
416}