Skip to content

Commit 38102c8

Browse files
committed
feat(mmr): Get auth-path node indices
Add a fairly fast function to get the node indices for an authentication path for a leaf.
1 parent 2dba318 commit 38102c8

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,40 @@ pub fn node_indices_added_by_append(old_leaf_count: u64) -> Vec<u64> {
112112
added_node_indices
113113
}
114114

115+
/// Get the node indices of the authentication path starting from the specified
116+
/// leaf, to its peak.
117+
pub fn auth_path_node_indices(num_leafs: u64, leaf_index: u64) -> Vec<u64> {
118+
assert!(
119+
leaf_index < num_leafs,
120+
"Leaf index out-of-bounds: {leaf_index}/{num_leafs}"
121+
);
122+
123+
let (mut merkle_tree_index, _) = leaf_index_to_mt_index_and_peak_index(leaf_index, num_leafs);
124+
let mut node_index = leaf_index_to_node_index(leaf_index);
125+
let mut height = 0;
126+
let tree_height = u64::BITS - merkle_tree_index.leading_zeros() - 1;
127+
let mut ret = Vec::with_capacity(tree_height as usize);
128+
while merkle_tree_index > 1 {
129+
let is_left_sibling = merkle_tree_index & 1 == 0;
130+
let height_pow = 1u64 << (height + 1);
131+
let as_1_or_minus_1: u64 = (2 * (is_left_sibling as i64) - 1) as u64;
132+
let signed_height_pow = height_pow.wrapping_mul(as_1_or_minus_1);
133+
let sibling = node_index
134+
.wrapping_add(signed_height_pow)
135+
.wrapping_sub(as_1_or_minus_1);
136+
137+
node_index += 1 << ((height + 1) * is_left_sibling as u32);
138+
139+
ret.push(sibling);
140+
merkle_tree_index >>= 1;
141+
height += 1;
142+
}
143+
144+
debug_assert_eq!(tree_height, ret.len() as u32, "Allocation must be optimal");
145+
146+
ret
147+
}
148+
115149
/// Get the node indices of the authentication path hash digest needed
116150
/// to calculate the digest of `peak_node_index` from `start_node_index`
117151
pub fn get_authentication_path_node_indices(
@@ -506,6 +540,40 @@ mod mmr_test {
506540
}
507541
}
508542

543+
#[test]
544+
fn auth_path_indices_unit_test() {
545+
assert_eq!(vec![2, 6, 14, 30], auth_path_node_indices(16, 0));
546+
assert_eq!(vec![1, 6, 14, 30], auth_path_node_indices(16, 1));
547+
assert_eq!(vec![5, 3, 14, 30], auth_path_node_indices(16, 2));
548+
assert_eq!(vec![4, 3, 14, 30], auth_path_node_indices(16, 3));
549+
assert_eq!(vec![9, 13, 7, 30], auth_path_node_indices(16, 4));
550+
assert_eq!(vec![8, 13, 7, 30], auth_path_node_indices(16, 5));
551+
assert_eq!(vec![12, 10, 7, 30], auth_path_node_indices(16, 6));
552+
assert_eq!(vec![11, 10, 7, 30], auth_path_node_indices(16, 7));
553+
assert_eq!(vec![17, 21, 29, 15], auth_path_node_indices(16, 8));
554+
assert_eq!(vec![16, 21, 29, 15], auth_path_node_indices(16, 9));
555+
assert_eq!(vec![20, 18, 29, 15], auth_path_node_indices(16, 10));
556+
assert_eq!(vec![19, 18, 29, 15], auth_path_node_indices(16, 11));
557+
assert_eq!(vec![24, 28, 22, 15], auth_path_node_indices(16, 12));
558+
assert_eq!(vec![23, 28, 22, 15], auth_path_node_indices(16, 13));
559+
assert_eq!(vec![27, 25, 22, 15], auth_path_node_indices(16, 14));
560+
assert_eq!(vec![26, 25, 22, 15], auth_path_node_indices(16, 15));
561+
562+
assert_eq!(Vec::<u64>::new(), auth_path_node_indices(1, 0));
563+
assert_eq!(vec![2], auth_path_node_indices(2, 0));
564+
assert_eq!(vec![1], auth_path_node_indices(2, 1));
565+
566+
let mut expected = vec![];
567+
for i in 1..63 {
568+
expected.push((1u64 << (i + 1)) - 2);
569+
assert_eq!(
570+
expected,
571+
auth_path_node_indices(1 << i, 0),
572+
"must match for i={i}"
573+
);
574+
}
575+
}
576+
509577
#[proptest]
510578
fn right_lineage_length_from_node_index_does_not_crash(node_index: u64) {
511579
right_lineage_length_from_node_index(node_index);

0 commit comments

Comments
 (0)