Skip to content

Commit c68f9f9

Browse files
committed
mmr_authentication_struct: Build multiple witnesses from one MMR
Add a struct for encapulating the Merkle authentication struct authenticity witness and one for encapsulating the auth struct, its withness, and the data, which are the indexed leaves.
1 parent b85840f commit c68f9f9

File tree

1 file changed

+198
-102
lines changed

1 file changed

+198
-102
lines changed

twenty-first/src/util_types/mmr/mmr_authentication_struct.rs

Lines changed: 198 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,24 @@ use super::mmr_accumulator::MmrAccumulator;
2323

2424
const ROOT_MT_INDEX: u64 = 1;
2525

26+
/// A witness to facilitate the proving of the authenticity of a Merkle
27+
/// authentication struct.
28+
#[derive(Debug, Clone)]
2629
pub struct MerkleAuthenticationStructAuthenticityWitness {
2730
// All indices are Merkle tree node indices
28-
nd_auth_struct_indices: Vec<u64>,
29-
nd_sibling_indices: Vec<(u64, u64)>,
30-
nd_siblings: Vec<(Digest, Digest)>,
31+
pub nd_auth_struct_indices: Vec<u64>,
32+
pub nd_sibling_indices: Vec<(u64, u64)>,
33+
pub nd_siblings: Vec<(Digest, Digest)>,
34+
}
35+
36+
/// An authentication structure that can be used to prove membership of a list
37+
/// of leaves in a Merkle tree, along with the indexed leaves in question, and
38+
/// the witness necessary to prove membership in a ZK program.
39+
#[derive(Debug, Clone)]
40+
pub struct AuthenticatedMerkleAuthStruct {
41+
pub auth_struct: Vec<Digest>,
42+
pub indexed_leafs: Vec<(u64, Digest)>,
43+
pub witness: MerkleAuthenticationStructAuthenticityWitness,
3144
}
3245

3346
impl MerkleAuthenticationStructAuthenticityWitness {
@@ -190,103 +203,120 @@ impl MerkleAuthenticationStructAuthenticityWitness {
190203
pub fn new_from_mmr_membership_proofs(
191204
mmra: &MmrAccumulator,
192205
indexed_mmr_mps: Vec<(u64, Digest, MmrMembershipProof)>,
193-
) -> (Self, Vec<Digest>, Vec<(u64, Digest)>) {
194-
// TODO: Consider rewriting this to return a list of authenticated
195-
// authentication structs, one for each peak in question.
206+
) -> HashMap<u32, AuthenticatedMerkleAuthStruct> {
207+
#[derive(Clone, Debug)]
208+
struct IndexedAuthenticatedMmrLeaf {
209+
merkle_tree_node_index: u64,
210+
merkle_tree_leaf_index: u64,
211+
leaf_digest: Digest,
212+
membership_proof: MmrMembershipProof,
213+
}
196214

197-
// Verify that MMR leaf indices belong to the same peak
215+
// Split indexed MMR-mps into a hashmap with one entry for each
216+
// referenced peak in the MMR.
198217
let num_mmr_leafs = mmra.num_leafs();
199-
let mt_and_peak_indices = indexed_mmr_mps
200-
.iter()
201-
.map(|(mmr_leaf_index, _leaf, _mmr_mp)| {
202-
leaf_index_to_mt_index_and_peak_index(*mmr_leaf_index, num_mmr_leafs)
203-
})
204-
.collect_vec();
218+
let mut peak_index_to_indexed_mmr_mp: HashMap<u32, Vec<IndexedAuthenticatedMmrLeaf>> =
219+
HashMap::default();
220+
let peak_heights = get_peak_heights(num_mmr_leafs);
221+
for (mmr_leaf_index, leaf, mmr_mp) in indexed_mmr_mps {
222+
let (mt_index, peak_index) =
223+
leaf_index_to_mt_index_and_peak_index(mmr_leaf_index, num_mmr_leafs);
224+
let peak_index_as_usize: usize = peak_index.try_into().unwrap();
225+
let num_leafs_local_mt = 1 << peak_heights[peak_index_as_usize];
226+
let mt_leaf_index = mt_index - num_leafs_local_mt;
227+
peak_index_to_indexed_mmr_mp
228+
.entry(peak_index)
229+
.or_default()
230+
.push(IndexedAuthenticatedMmrLeaf {
231+
merkle_tree_node_index: mt_index,
232+
merkle_tree_leaf_index: mt_leaf_index,
233+
leaf_digest: leaf,
234+
membership_proof: mmr_mp,
235+
});
236+
}
205237

206-
assert!(
207-
mt_and_peak_indices
238+
// Loop over all peaks and collect an authentication witness struct
239+
// for each peak.
240+
let mut peak_index_to_authenticated_auth_struct = HashMap::default();
241+
for (peak_index, indexed_mmr_mp_structs) in peak_index_to_indexed_mmr_mp {
242+
let peak_index_as_usize: usize = peak_index.try_into().unwrap();
243+
let num_leafs_in_local_mt = 1 << peak_heights[peak_index_as_usize];
244+
let local_mt_leaf_indices = indexed_mmr_mp_structs
208245
.iter()
209-
.map(|(_mt_index, peak_index)| peak_index)
210-
.unique()
211-
.count()
212-
< 2
213-
);
214-
assert!(!mt_and_peak_indices.is_empty(), "");
215-
216-
let peak_index = mt_and_peak_indices[0].1;
217-
let mt_indices = mt_and_peak_indices
218-
.into_iter()
219-
.map(|(mt_index, _peak_index)| mt_index)
220-
.collect_vec();
221-
222-
let peak_index: usize = peak_index.try_into().unwrap();
223-
let height_of_local_mt = get_peak_heights(num_mmr_leafs)[peak_index];
224-
let num_leafs_in_local_mt = 1 << height_of_local_mt;
225-
let local_mt_leaf_indices = mt_indices
226-
.iter()
227-
.map(|mt_index| mt_index - num_leafs_in_local_mt)
228-
.collect_vec();
229-
230-
let (nd_auth_struct_indices, nd_sibling_indices) =
231-
Self::auth_struct_and_nd_indices(num_leafs_in_local_mt, &local_mt_leaf_indices);
232-
233-
// Collect all node digests that can be calculated
234-
let peak = mmra.peaks()[peak_index];
235-
let mut node_digests: HashMap<u64, Digest> = HashMap::default();
236-
node_digests.insert(ROOT_MT_INDEX, peak);
237-
for ((_mmr_leaf_index, mut node, mmr_mp), mut mt_index) in indexed_mmr_mps
238-
.clone()
239-
.into_iter()
240-
.zip_eq(mt_indices.clone())
241-
{
242-
for ap_elem in mmr_mp.authentication_path.iter() {
243-
node_digests.insert(mt_index, node);
244-
node_digests.insert(mt_index ^ 1, *ap_elem);
245-
node = if mt_index & 1 == 0 {
246-
Tip5::hash_pair(node, *ap_elem)
247-
} else {
248-
Tip5::hash_pair(*ap_elem, node)
249-
};
250-
251-
mt_index /= 2;
246+
.map(|x| x.merkle_tree_leaf_index)
247+
.collect_vec();
248+
249+
let (nd_auth_struct_indices, nd_sibling_indices) =
250+
Self::auth_struct_and_nd_indices(num_leafs_in_local_mt, &local_mt_leaf_indices);
251+
let peak = mmra.peaks()[peak_index_as_usize];
252+
253+
let mut node_digests: HashMap<u64, Digest> = HashMap::default();
254+
node_digests.insert(ROOT_MT_INDEX, peak);
255+
256+
// Loop over all indexed leafs for this peak
257+
for indexed_mmr_mp in indexed_mmr_mp_structs.iter() {
258+
let mut mt_node_index = indexed_mmr_mp.merkle_tree_node_index;
259+
let mut node = indexed_mmr_mp.leaf_digest;
260+
261+
// Loop over all authentication path elements for this indexed leaf
262+
for ap_elem in indexed_mmr_mp.membership_proof.authentication_path.iter() {
263+
node_digests.insert(mt_node_index, node);
264+
node_digests.insert(mt_node_index ^ 1, *ap_elem);
265+
node = if mt_node_index & 1 == 0 {
266+
Tip5::hash_pair(node, *ap_elem)
267+
} else {
268+
Tip5::hash_pair(*ap_elem, node)
269+
};
270+
271+
mt_node_index /= 2;
272+
}
273+
274+
// Sanity check that MMR-MPs are valid
275+
assert_eq!(peak, node, "Derived peak must match provided peak");
252276
}
253-
254-
// Sanity check that MMR-MPs are valid
255-
assert_eq!(peak, node, "Derived peak must match provided peak");
277+
let nd_siblings = nd_sibling_indices
278+
.iter()
279+
.map(|(left_idx, right_idx)| (node_digests[left_idx], node_digests[right_idx]))
280+
.collect_vec();
281+
let auth_struct = nd_auth_struct_indices
282+
.iter()
283+
.map(|idx| node_digests[idx])
284+
.collect_vec();
285+
let indexed_leafs = indexed_mmr_mp_structs
286+
.into_iter()
287+
.map(|indexed_mmr_mp| {
288+
(
289+
indexed_mmr_mp.merkle_tree_leaf_index,
290+
indexed_mmr_mp.leaf_digest,
291+
)
292+
})
293+
.collect_vec();
294+
295+
let witness = Self {
296+
nd_auth_struct_indices,
297+
nd_sibling_indices,
298+
nd_siblings,
299+
};
300+
301+
peak_index_to_authenticated_auth_struct.insert(
302+
peak_index,
303+
AuthenticatedMerkleAuthStruct {
304+
auth_struct,
305+
indexed_leafs,
306+
witness,
307+
},
308+
);
256309
}
257310

258-
let nd_siblings = nd_sibling_indices
259-
.iter()
260-
.map(|(left_idx, right_idx)| (node_digests[left_idx], node_digests[right_idx]))
261-
.collect_vec();
262-
let auth_struct = nd_auth_struct_indices
263-
.iter()
264-
.map(|idx| node_digests[idx])
265-
.collect_vec();
266-
let indexed_leafs = local_mt_leaf_indices
267-
.into_iter()
268-
.zip_eq(
269-
indexed_mmr_mps
270-
.into_iter()
271-
.map(|(_mmr_leaf_index, leaf, _mmr_mp)| leaf),
272-
)
273-
.collect_vec();
274-
275-
let auth_struct_witness = Self {
276-
nd_auth_struct_indices,
277-
nd_sibling_indices,
278-
nd_siblings,
279-
};
280-
281-
(auth_struct_witness, auth_struct, indexed_leafs)
311+
peak_index_to_authenticated_auth_struct
282312
}
283313

284314
/// Return the authentication structure witness, authentication structure,
285315
/// and the (leaf-index, leaf-digest) pairs.
286316
pub fn new_from_merkle_tree(
287317
tree: &MerkleTree<Tip5>,
288318
mut revealed_leaf_indices: Vec<u64>,
289-
) -> (Self, Vec<Digest>, Vec<(u64, Digest)>) {
319+
) -> AuthenticatedMerkleAuthStruct {
290320
revealed_leaf_indices.sort_unstable();
291321
revealed_leaf_indices.dedup();
292322
revealed_leaf_indices.reverse();
@@ -322,13 +352,17 @@ impl MerkleAuthenticationStructAuthenticityWitness {
322352
.map(|node_index| tree.node(*node_index as usize).unwrap())
323353
.collect_vec();
324354

325-
let auth_struct_witness = Self {
355+
let witness = Self {
326356
nd_auth_struct_indices,
327357
nd_sibling_indices,
328358
nd_siblings,
329359
};
330360

331-
(auth_struct_witness, auth_struct, indexed_leafs)
361+
AuthenticatedMerkleAuthStruct {
362+
auth_struct,
363+
indexed_leafs,
364+
witness,
365+
}
332366
}
333367
}
334368

@@ -345,6 +379,48 @@ mod tests {
345379

346380
use super::*;
347381

382+
#[proptest(cases = 20)]
383+
fn root_from_authentication_struct_mmr_prop_test(
384+
#[strategy(0..u64::MAX / 2)] mmr_leaf_count: u64,
385+
#[strategy(0usize..20)] _num_revealed_leafs: usize,
386+
#[strategy(vec(0u64..#mmr_leaf_count, #_num_revealed_leafs))]
387+
mmr_revealed_leaf_indices: Vec<u64>,
388+
) {
389+
let indexed_leafs_input: Vec<(u64, Digest)> = mmr_revealed_leaf_indices
390+
.iter()
391+
.map(|idx| (*idx, random()))
392+
.collect_vec();
393+
let (mmra, mmr_mps) = mmra_with_mps(mmr_leaf_count, indexed_leafs_input.clone());
394+
let indexed_mmr_mps = mmr_mps
395+
.into_iter()
396+
.zip_eq(indexed_leafs_input)
397+
.map(|(mmr_mp, (idx, leaf))| (idx, leaf, mmr_mp))
398+
.collect_vec();
399+
400+
let authenticated_auth_structs =
401+
MerkleAuthenticationStructAuthenticityWitness::new_from_mmr_membership_proofs(
402+
&mmra,
403+
indexed_mmr_mps,
404+
);
405+
406+
let peak_heights = get_peak_heights(mmr_leaf_count);
407+
for (peak_index, authentication_auth_struct) in authenticated_auth_structs {
408+
let AuthenticatedMerkleAuthStruct {
409+
auth_struct,
410+
indexed_leafs,
411+
witness,
412+
} = &authentication_auth_struct;
413+
let tree_height: u32 = peak_heights[peak_index as usize];
414+
let computed_root = witness.root_from_authentication_struct(
415+
tree_height,
416+
auth_struct.to_owned(),
417+
indexed_leafs.to_owned(),
418+
);
419+
420+
prop_assert_eq!(mmra.peaks()[peak_index as usize], computed_root);
421+
}
422+
}
423+
348424
#[test]
349425
fn auth_struct_from_mmr_mps_test_height_5_9_indices() {
350426
let local_tree_height = 5;
@@ -360,17 +436,26 @@ mod tests {
360436
.map(|(mmr_mp, (idx, leaf))| (idx, leaf, mmr_mp))
361437
.collect_vec();
362438

363-
let (mmr_auth_struct, auth_struct, indexed_leafs) =
439+
let authenticity_witnesses =
364440
MerkleAuthenticationStructAuthenticityWitness::new_from_mmr_membership_proofs(
365441
&mmra,
366442
indexed_mmr_mps,
367443
);
444+
assert!(
445+
authenticity_witnesses.len().is_one(),
446+
"All indices belong to first peak"
447+
);
448+
let AuthenticatedMerkleAuthStruct {
449+
auth_struct,
450+
indexed_leafs,
451+
witness,
452+
} = &authenticity_witnesses[&0];
368453

369454
let tree_height: u32 = local_tree_height.try_into().unwrap();
370-
let computed_root = mmr_auth_struct.root_from_authentication_struct(
455+
let computed_root = witness.root_from_authentication_struct(
371456
tree_height,
372-
auth_struct,
373-
indexed_leafs,
457+
auth_struct.to_owned(),
458+
indexed_leafs.to_owned(),
374459
);
375460

376461
let peak_index = 0;
@@ -393,17 +478,26 @@ mod tests {
393478
.map(|(mmr_mp, (idx, leaf))| (idx, leaf, mmr_mp))
394479
.collect_vec();
395480

396-
let (mmr_auth_struct, auth_struct, indexed_leafs) =
481+
let authenticity_witnesses =
397482
MerkleAuthenticationStructAuthenticityWitness::new_from_mmr_membership_proofs(
398483
&mmra,
399484
indexed_mmr_mps,
400485
);
486+
assert!(
487+
authenticity_witnesses.len().is_one(),
488+
"All indices belong to first peak"
489+
);
490+
let AuthenticatedMerkleAuthStruct {
491+
auth_struct,
492+
indexed_leafs,
493+
witness,
494+
} = &authenticity_witnesses[&0];
401495

402496
let tree_height: u32 = local_tree_height.try_into().unwrap();
403-
let computed_root = mmr_auth_struct.root_from_authentication_struct(
497+
let computed_root = witness.root_from_authentication_struct(
404498
tree_height,
405-
auth_struct,
406-
indexed_leafs,
499+
auth_struct.to_owned(),
500+
indexed_leafs.to_owned(),
407501
);
408502

409503
let peak_index = 0;
@@ -423,18 +517,20 @@ mod tests {
423517
let leafs: Vec<Digest> = random_elements(num_leafs.try_into().unwrap());
424518
let tree = MerkleTree::<Tip5>::new::<CpuParallel>(&leafs).unwrap();
425519

426-
let (mmr_auth_struct, auth_struct, indexed_leafs) =
520+
let authenticated_auth_struct =
427521
MerkleAuthenticationStructAuthenticityWitness::new_from_merkle_tree(
428522
&tree,
429523
revealed_leaf_indices,
430524
);
431-
432-
let tree_height: u32 = tree_height.try_into().unwrap();
433-
let computed_root = mmr_auth_struct.root_from_authentication_struct(
434-
tree_height,
525+
let AuthenticatedMerkleAuthStruct {
435526
auth_struct,
436527
indexed_leafs,
437-
);
528+
witness,
529+
} = authenticated_auth_struct;
530+
531+
let tree_height: u32 = tree_height.try_into().unwrap();
532+
let computed_root =
533+
witness.root_from_authentication_struct(tree_height, auth_struct, indexed_leafs);
438534
let expected_root = tree.root();
439535
prop_assert_eq!(expected_root, computed_root);
440536
}

0 commit comments

Comments
 (0)