cumulus_primitives_spec_messaging/
mmr.rs1use alloc::vec::Vec;
30use codec::{Decode, DecodeWithMemTracking, Encode, Error as CodecError, Input, MaxEncodedLen};
31use mmr_lib::{ancestry_proof::bagging_peaks_hashes, Error as MmrError, Merge};
32use polkadot_core_primitives::Hash;
33use scale_info::TypeInfo;
34use sp_core::ConstU32;
35use sp_runtime::{traits::Hash as HashT, BoundedVec};
36
37use crate::{SpecHasher, INNER_TAG, PEAK_TAG, ROOT_TAG};
38
39pub const MAX_MMR_LEAF_COUNT: u64 = 1 << 48;
42
43pub const MAX_MMR_HASHES: u32 = 64;
46
47#[derive(
49 Clone,
50 Copy,
51 Encode,
52 Decode,
53 DecodeWithMemTracking,
54 MaxEncodedLen,
55 PartialEq,
56 Eq,
57 PartialOrd,
58 Ord,
59 Debug,
60 Default,
61 TypeInfo,
62)]
63pub struct MessagePosition(pub u64);
64
65pub struct SpecMerge;
67
68impl Merge for SpecMerge {
69 type Item = Hash;
70
71 fn merge(left: &Hash, right: &Hash) -> Result<Hash, MmrError> {
72 Ok(tagged_node(INNER_TAG, left, right))
73 }
74
75 fn merge_peaks(left: &Hash, right: &Hash) -> Result<Hash, MmrError> {
77 Ok(tagged_node(PEAK_TAG, left, right))
78 }
79}
80
81fn tagged_node(tag: u8, left: &Hash, right: &Hash) -> Hash {
82 let mut preimage = [0u8; 1 + 32 + 32];
83 preimage[0] = tag;
84 preimage[1..33].copy_from_slice(left.as_bytes());
85 preimage[33..65].copy_from_slice(right.as_bytes());
86 <SpecHasher as HashT>::hash(&preimage)
87}
88
89pub fn empty_root() -> MmrRoot {
91 MmrRoot::from_bag(0, Hash::zero())
92}
93
94pub fn bag_peaks(peaks: &[Hash]) -> Hash {
98 bagging_peaks_hashes::<Hash, SpecMerge>(peaks.to_vec()).unwrap_or_default()
100}
101
102#[derive(Clone, Copy, PartialEq, Eq, Debug)]
104pub struct FrontierFull;
105
106#[derive(Clone, Copy, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Debug, TypeInfo)]
109pub struct MmrRoot(pub Hash);
110
111impl MmrRoot {
112 pub fn from_bag(leaf_count: u64, bag: Hash) -> Self {
116 let mut preimage = [0u8; 1 + 8 + 32];
117 preimage[0] = ROOT_TAG;
118 preimage[1..9].copy_from_slice(&leaf_count.to_le_bytes());
119 preimage[9..41].copy_from_slice(bag.as_bytes());
120 Self(<SpecHasher as HashT>::hash(&preimage))
121 }
122}
123
124#[derive(Clone, Encode, PartialEq, Eq, Debug, TypeInfo, Default)]
137pub struct MmrFrontier {
138 leaf_count: u64,
140 peaks: BoundedVec<Hash, ConstU32<MAX_MMR_HASHES>>,
142}
143
144impl MmrFrontier {
145 pub fn new() -> Self {
146 Self::default()
147 }
148
149 pub fn from_parts(peaks: Vec<Hash>, leaf_count: u64) -> Option<Self> {
152 if leaf_count > MAX_MMR_LEAF_COUNT || peaks.len() != leaf_count.count_ones() as usize {
153 return None;
154 }
155 BoundedVec::try_from(peaks).ok().map(|peaks| Self { leaf_count, peaks })
157 }
158
159 pub fn peaks(&self) -> &[Hash] {
161 &self.peaks
162 }
163
164 pub fn leaf_count(&self) -> u64 {
165 self.leaf_count
166 }
167
168 pub fn try_append(&mut self, leaf: Hash) -> Result<(), FrontierFull> {
171 if self.leaf_count >= MAX_MMR_LEAF_COUNT {
172 return Err(FrontierFull);
173 }
174 let mut node = leaf;
175 let merges = (self.leaf_count + 1).trailing_zeros();
178 for _ in 0..merges {
179 let left = self
180 .peaks
181 .pop()
182 .expect("the constructors enforce one peak per set bit of `leaf_count`; qed");
183 node = <SpecMerge as Merge>::merge(&left, &node).expect("SpecMerge is infallible; qed");
184 }
185 self.peaks
186 .try_push(node)
187 .expect("a `u64` leaf count never has more than 64 set bits; qed");
188 self.leaf_count += 1;
189 Ok(())
190 }
191
192 pub fn append(&mut self, leaf: Hash) {
200 self.try_append(leaf)
201 .expect("a frontier built leaf by leaf never reaches `MAX_MMR_LEAF_COUNT`; qed")
202 }
203
204 pub fn root(&self) -> MmrRoot {
208 MmrRoot::from_bag(self.leaf_count, bag_peaks(&self.peaks))
209 }
210
211 pub(crate) fn mmr_size(&self) -> u64 {
214 mmr_size(self.leaf_count)
215 }
216}
217
218pub(crate) fn mmr_size(leaf_count: u64) -> u64 {
221 if leaf_count == 0 {
222 0
223 } else {
224 mmr_lib::leaf_index_to_mmr_size(leaf_count - 1)
225 }
226}
227
228impl Decode for MmrFrontier {
232 fn decode<I: Input>(input: &mut I) -> Result<Self, CodecError> {
233 let leaf_count = u64::decode(input)?;
234 let peaks = BoundedVec::<Hash, ConstU32<MAX_MMR_HASHES>>::decode(input)?;
235 Self::from_parts(peaks.into_inner(), leaf_count)
236 .ok_or_else(|| "MmrFrontier: inconsistent peaks / leaf count".into())
237 }
238}
239
240impl DecodeWithMemTracking for MmrFrontier {}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use mmr_lib::{
246 leaf_index_to_pos,
247 util::{MemMMR, MemStore},
248 MerkleProof,
249 };
250 fn h(byte: u8) -> Hash {
251 Hash::repeat_byte(byte)
252 }
253
254 #[test]
255 fn merge_and_merge_peaks_are_domain_separated() {
256 let a = h(1);
257 let b = h(2);
258 assert_ne!(
260 <SpecMerge as Merge>::merge(&a, &b).unwrap(),
261 <SpecMerge as Merge>::merge_peaks(&a, &b).unwrap()
262 );
263 }
264
265 #[test]
266 fn merge_is_order_sensitive() {
267 let a = h(1);
268 let b = h(2);
269 assert_ne!(
270 <SpecMerge as Merge>::merge(&a, &b).unwrap(),
271 <SpecMerge as Merge>::merge(&b, &a).unwrap()
272 );
273 }
274
275 #[test]
276 fn from_parts_enforces_append_invariants() {
277 assert!(MmrFrontier::from_parts(Vec::new(), 0).is_some());
279 assert!(MmrFrontier::from_parts(vec![h(1)], 1).is_some());
280 assert!(MmrFrontier::from_parts(vec![h(1), h(2)], 3).is_some());
281 assert!(MmrFrontier::from_parts(Vec::new(), 1).is_none());
283 assert!(MmrFrontier::from_parts(vec![h(1)], 3).is_none());
284 assert!(MmrFrontier::from_parts(vec![h(1), h(2)], 1).is_none());
285 assert!(MmrFrontier::from_parts(vec![h(1)], MAX_MMR_LEAF_COUNT).is_some());
287 assert!(MmrFrontier::from_parts(vec![h(1)], 1 << 63).is_none());
288 assert!(MmrFrontier::from_parts(vec![h(1); MAX_MMR_HASHES as usize], u64::MAX).is_none());
289 }
290
291 #[test]
292 fn accumulator_root_matches_mmr_lib() {
293 let mut acc = MmrFrontier::new();
296 let store = MemStore::<Hash>::default();
297 let mut reference = MemMMR::<Hash, SpecMerge>::new(0, &store);
298 for i in 1..=5u8 {
299 acc.append(h(i));
300 reference.push(h(i)).unwrap();
301 assert_eq!(acc.peaks().len() as u32, acc.leaf_count().count_ones());
302 }
303 assert_eq!(acc.root(), MmrRoot::from_bag(5, reference.get_root().unwrap()));
304 }
305
306 #[test]
307 fn empty_accumulator_root_is_empty_root() {
308 assert_eq!(bag_peaks(&[]), Hash::zero());
311 assert_eq!(MmrFrontier::new().root(), empty_root());
312 assert_eq!(MmrFrontier::from_parts(Vec::new(), 0).unwrap().root(), empty_root());
313 }
314
315 #[test]
316 fn decode_enforces_the_same_invariants_as_from_parts() {
317 let good = MmrFrontier::from_parts(vec![h(1), h(2)], 3).unwrap();
320 assert_eq!(MmrFrontier::decode(&mut &good.encode()[..]).unwrap(), good);
321 let bad_shape = (3u64, vec![h(1)]).encode();
323 assert!(MmrFrontier::decode(&mut &bad_shape[..]).is_err());
324 let too_large = (1u64 << 63, vec![h(1)]).encode();
325 assert!(MmrFrontier::decode(&mut &too_large[..]).is_err());
326 let too_many = (u64::MAX, vec![h(1); 65]).encode();
328 assert!(MmrFrontier::decode(&mut &too_many[..]).is_err());
329 assert_eq!(good.encode(), (3u64, vec![h(1), h(2)]).encode());
331 }
332
333 #[test]
334 fn empty_root_frozen_vector() {
335 assert_eq!(
338 empty_root().0,
339 Hash::from(hex_literal::hex!(
340 "3efd2c3528b8f163c2e2349211af73f86181b748be1b6a5eebfbadc9428643ce"
341 ))
342 );
343 }
344
345 #[test]
346 fn mmr_root_frozen_vector() {
347 let mut acc = MmrFrontier::new();
350 for i in 1..=5u8 {
351 acc.append(h(i));
352 }
353 assert_eq!(
354 acc.root().0,
355 Hash::from(hex_literal::hex!(
356 "1190cb02577a8bafec9f05c38e64ca9155b65e9d862604c4f3d920dff4e5a85f"
357 ))
358 );
359 }
360
361 #[test]
362 fn mmr_root_frozen_vectors_at_64_and_65_leaves() {
363 let mut acc = MmrFrontier::new();
366 for i in 1..=64u8 {
367 acc.append(h(i));
368 }
369 assert_eq!(acc.peaks().len(), 1);
370 assert_eq!(
371 acc.root().0,
372 Hash::from(hex_literal::hex!(
373 "41cec1de3c0e0af93a02932404fc195f0fbb56f542abbce0681b4632da0848c2"
374 ))
375 );
376 acc.append(h(65));
377 assert_eq!(acc.peaks().len(), 2);
378 assert_eq!(
379 acc.root().0,
380 Hash::from(hex_literal::hex!(
381 "109ccf71e0b753f6ff76d7f48e59f6376c1347bea4556b20c4a453a2cb8ca28d"
382 ))
383 );
384 }
385
386 #[test]
387 fn no_append_carries_a_frontier_past_the_ceiling() {
388 let mut full = MmrFrontier::from_parts(vec![h(1)], MAX_MMR_LEAF_COUNT).unwrap();
391 assert_eq!(full.try_append(h(2)), Err(FrontierFull));
392 assert_eq!(full.leaf_count(), MAX_MMR_LEAF_COUNT);
393 assert_eq!(MmrFrontier::decode(&mut &full.encode()[..]).unwrap(), full);
394
395 let below = MAX_MMR_LEAF_COUNT - 1;
396 let mut last =
397 MmrFrontier::from_parts(vec![h(1); below.count_ones() as usize], below).unwrap();
398 assert_eq!(last.try_append(h(2)), Ok(()));
399 assert_eq!(last.leaf_count(), MAX_MMR_LEAF_COUNT);
400 assert_eq!(MmrFrontier::decode(&mut &last.encode()[..]).unwrap(), last);
401 }
402
403 #[test]
404 fn inclusion_proof_round_trips() {
405 let store = MemStore::<Hash>::default();
406 let mut mmr = MemMMR::<Hash, SpecMerge>::new(0, &store);
407 let positions: Vec<u64> = (0..6u8).map(|i| mmr.push(h(i)).unwrap()).collect();
408 let root = mmr.get_root().unwrap();
409
410 let proof: MerkleProof<Hash, SpecMerge> =
411 mmr.gen_proof(vec![leaf_index_to_pos(1), leaf_index_to_pos(4)]).unwrap();
412
413 assert!(proof.verify(root, vec![(positions[1], h(1)), (positions[4], h(4))]).unwrap());
414 assert!(!proof.verify(root, vec![(positions[1], h(99)), (positions[4], h(4))]).unwrap());
415 }
416}