mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
refactor pmmr functions, improve efficiency; panic on 0 pos1 (#3663)
This commit is contained in:
parent
3f4f165e0b
commit
4aaa3344e6
2 changed files with 118 additions and 129 deletions
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::{cmp::max, iter, marker, ops::Range, u64};
|
use std::{iter, marker, ops::Range, u64};
|
||||||
|
|
||||||
use croaring::Bitmap;
|
use croaring::Bitmap;
|
||||||
|
|
||||||
|
@ -22,9 +22,6 @@ use crate::core::pmmr::{Backend, ReadonlyPMMR};
|
||||||
use crate::core::BlockHeader;
|
use crate::core::BlockHeader;
|
||||||
use crate::ser::{PMMRIndexHashable, PMMRable};
|
use crate::ser::{PMMRIndexHashable, PMMRable};
|
||||||
|
|
||||||
/// 64 bits all ones: 0b11111111...1
|
|
||||||
const ALL_ONES: u64 = u64::MAX;
|
|
||||||
|
|
||||||
/// Trait with common methods for reading from a PMMR
|
/// Trait with common methods for reading from a PMMR
|
||||||
pub trait ReadablePMMR {
|
pub trait ReadablePMMR {
|
||||||
/// Leaf type
|
/// Leaf type
|
||||||
|
@ -448,57 +445,102 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 64 bits all ones: 0b11111111...1
|
||||||
|
const ALL_ONES: u64 = u64::MAX;
|
||||||
|
|
||||||
|
/// peak bitmap and height of next node in mmr of given size
|
||||||
|
/// Example: on size 4 returns (0b11, 0) as mmr tree of size 4 is
|
||||||
|
/// 2
|
||||||
|
/// / \
|
||||||
|
/// 0 1 3
|
||||||
|
/// with 0b11 indicating the presence of peaks of height 0 and 1,
|
||||||
|
/// and 0 the height of the next node 4, which is a leaf
|
||||||
|
/// NOTE:
|
||||||
|
/// the peak map also encodes the path taken from the root to the added node
|
||||||
|
/// since the path turns left (resp. right) if-and-only-if
|
||||||
|
/// a peak at that height is absent (resp. present)
|
||||||
|
pub fn peak_map_height(mut size: u64) -> (u64, u64) {
|
||||||
|
if size == 0 {
|
||||||
|
// rust can't shift right by 64
|
||||||
|
return (0, 0);
|
||||||
|
}
|
||||||
|
let mut peak_size = ALL_ONES >> size.leading_zeros();
|
||||||
|
let mut peak_map = 0;
|
||||||
|
while peak_size != 0 {
|
||||||
|
peak_map <<= 1;
|
||||||
|
if size >= peak_size {
|
||||||
|
size -= peak_size;
|
||||||
|
peak_map |= 1;
|
||||||
|
}
|
||||||
|
peak_size >>= 1;
|
||||||
|
}
|
||||||
|
(peak_map, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sizes of peaks and height of next node in mmr of given size
|
||||||
|
/// similar to peak_map_height but replacing bitmap by vector of sizes
|
||||||
|
/// Example: on input 5 returns ([3,1], 1) as mmr state before adding 5 was
|
||||||
|
/// 2
|
||||||
|
/// / \
|
||||||
|
/// 0 1 3 4
|
||||||
|
pub fn peak_sizes_height(mut size: u64) -> (Vec<u64>, u64) {
|
||||||
|
if size == 0 {
|
||||||
|
// rust can't shift right by 64
|
||||||
|
return (vec![], 0);
|
||||||
|
}
|
||||||
|
let mut peak_size = ALL_ONES >> size.leading_zeros();
|
||||||
|
let mut peak_sizes = vec![];
|
||||||
|
while peak_size != 0 {
|
||||||
|
if size >= peak_size {
|
||||||
|
peak_sizes.push(peak_size);
|
||||||
|
size -= peak_size;
|
||||||
|
}
|
||||||
|
peak_size >>= 1;
|
||||||
|
}
|
||||||
|
(peak_sizes, size)
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the postorder traversal index of all peaks in a MMR given its size.
|
/// Gets the postorder traversal index of all peaks in a MMR given its size.
|
||||||
/// Starts with the top peak, which is always on the left
|
/// Starts with the top peak, which is always on the left
|
||||||
/// side of the range, and navigates toward lower siblings toward the right
|
/// side of the range, and navigates toward lower siblings toward the right
|
||||||
/// of the range.
|
/// of the range.
|
||||||
pub fn peaks(num: u64) -> Vec<u64> {
|
/// For some odd reason, return empty when next node is not a leaf
|
||||||
if num == 0 {
|
pub fn peaks(size: u64) -> Vec<u64> {
|
||||||
return vec![];
|
let (peak_sizes, height) = peak_sizes_height(size);
|
||||||
|
if height == 0 {
|
||||||
|
peak_sizes
|
||||||
|
.iter()
|
||||||
|
.scan(0, |acc, &x| {
|
||||||
|
*acc += &x;
|
||||||
|
Some(*acc)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
}
|
}
|
||||||
let mut peak_size = ALL_ONES >> num.leading_zeros();
|
|
||||||
let mut num_left = num;
|
|
||||||
let mut sum_prev_peaks = 0;
|
|
||||||
let mut peaks = vec![];
|
|
||||||
while peak_size != 0 {
|
|
||||||
if num_left >= peak_size {
|
|
||||||
peaks.push(sum_prev_peaks + peak_size);
|
|
||||||
sum_prev_peaks += peak_size;
|
|
||||||
num_left -= peak_size;
|
|
||||||
}
|
}
|
||||||
peak_size >>= 1;
|
|
||||||
}
|
|
||||||
if num_left > 0 {
|
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
peaks
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The number of leaves in a MMR of the provided size.
|
/// The number of leaves in a MMR of the provided size.
|
||||||
pub fn n_leaves(size: u64) -> u64 {
|
pub fn n_leaves(size: u64) -> u64 {
|
||||||
let (sizes, height) = peak_sizes_height(size);
|
let (peak_map, height) = peak_map_height(size);
|
||||||
let nleaves = sizes.into_iter().map(|n| (n + 1) / 2 as u64).sum();
|
|
||||||
if height == 0 {
|
if height == 0 {
|
||||||
nleaves
|
peak_map
|
||||||
} else {
|
} else {
|
||||||
nleaves + 1
|
peak_map + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the pmmr index of the nth inserted element
|
/// Returns the 1-based pmmr index of 1-based leaf n
|
||||||
pub fn insertion_to_pmmr_index(mut sz: u64) -> u64 {
|
pub fn insertion_to_pmmr_index(nleaf1: u64) -> u64 {
|
||||||
if sz == 0 {
|
if nleaf1 == 0 {
|
||||||
return 0;
|
panic!("insertion_to_pmmr_index called with nleaf1 == 0");
|
||||||
}
|
}
|
||||||
// 1 based pmmrs
|
2 * (nleaf1 - 1) + 1 - (nleaf1 - 1).count_ones() as u64
|
||||||
sz -= 1;
|
|
||||||
2 * sz - sz.count_ones() as u64 + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the insertion index of the given leaf index
|
/// Returns the insertion index of the given leaf index
|
||||||
pub fn pmmr_leaf_to_insertion_index(pos1: u64) -> Option<u64> {
|
pub fn pmmr_leaf_to_insertion_index(pos1: u64) -> Option<u64> {
|
||||||
if pos1 == 0 {
|
if pos1 == 0 {
|
||||||
return None;
|
panic!("pmmr_leaf_to_insertion_index called with pos1 == 0");
|
||||||
}
|
}
|
||||||
let (insert_idx, height) = peak_map_height(pos1 - 1);
|
let (insert_idx, height) = peak_map_height(pos1 - 1);
|
||||||
if height == 0 {
|
if height == 0 {
|
||||||
|
@ -508,100 +550,47 @@ pub fn pmmr_leaf_to_insertion_index(pos1: u64) -> Option<u64> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// sizes of peaks and height of next node in mmr of given size
|
|
||||||
/// Example: on input 5 returns ([3,1], 1) as mmr state before adding 5 was
|
|
||||||
/// 2
|
|
||||||
/// / \
|
|
||||||
/// 0 1 3 4
|
|
||||||
pub fn peak_sizes_height(size: u64) -> (Vec<u64>, u64) {
|
|
||||||
if size == 0 {
|
|
||||||
return (vec![], 0);
|
|
||||||
}
|
|
||||||
let mut peak_size = ALL_ONES >> size.leading_zeros();
|
|
||||||
let mut sizes = vec![];
|
|
||||||
let mut size_left = size;
|
|
||||||
while peak_size != 0 {
|
|
||||||
if size_left >= peak_size {
|
|
||||||
sizes.push(peak_size);
|
|
||||||
size_left -= peak_size;
|
|
||||||
}
|
|
||||||
peak_size >>= 1;
|
|
||||||
}
|
|
||||||
(sizes, size_left)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return (peak_map, pos_height) of given 0-based node pos prior to its
|
|
||||||
/// addition
|
|
||||||
/// Example: on input 4 returns (0b11, 0) as mmr state before adding 4 was
|
|
||||||
/// 2
|
|
||||||
/// / \
|
|
||||||
/// 0 1 3
|
|
||||||
/// with 0b11 indicating presence of peaks of height 0 and 1.
|
|
||||||
/// NOTE:
|
|
||||||
/// the peak map also encodes the path taken from the root to the added node
|
|
||||||
/// since the path turns left (resp. right) if-and-only-if
|
|
||||||
/// a peak at that height is absent (resp. present)
|
|
||||||
pub fn peak_map_height(mut pos: u64) -> (u64, u64) {
|
|
||||||
if pos == 0 {
|
|
||||||
return (0, 0);
|
|
||||||
}
|
|
||||||
let mut peak_size = ALL_ONES >> pos.leading_zeros();
|
|
||||||
let mut bitmap = 0;
|
|
||||||
while peak_size != 0 {
|
|
||||||
bitmap <<= 1;
|
|
||||||
if pos >= peak_size {
|
|
||||||
pos -= peak_size;
|
|
||||||
bitmap |= 1;
|
|
||||||
}
|
|
||||||
peak_size >>= 1;
|
|
||||||
}
|
|
||||||
(bitmap, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The height of a node in a full binary tree from its postorder traversal
|
/// The height of a node in a full binary tree from its postorder traversal
|
||||||
/// index. This function is the base on which all others, as well as the MMR,
|
/// index.
|
||||||
/// are built.
|
pub fn bintree_postorder_height(pos1: u64) -> u64 {
|
||||||
pub fn bintree_postorder_height(num: u64) -> u64 {
|
if pos1 == 0 {
|
||||||
let mut pos = num.saturating_sub(1);
|
panic!("bintree_postorder_height called with pos1 == 0");
|
||||||
if pos == 0 {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
let mut peak_size = ALL_ONES >> pos.leading_zeros();
|
peak_map_height(pos1 - 1).1
|
||||||
while peak_size != 0 {
|
|
||||||
if pos >= peak_size {
|
|
||||||
pos -= peak_size;
|
|
||||||
}
|
|
||||||
peak_size >>= 1;
|
|
||||||
}
|
|
||||||
pos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is this position a leaf in the MMR?
|
/// Is this position a leaf in the MMR?
|
||||||
/// We know the positions of all leaves based on the postorder height of an MMR
|
/// We know the positions of all leaves based on the postorder height of an MMR
|
||||||
/// of any size (somewhat unintuitively but this is how the PMMR is "append
|
/// of any size (somewhat unintuitively but this is how the PMMR is "append
|
||||||
/// only").
|
/// only").
|
||||||
pub fn is_leaf(pos: u64) -> bool {
|
pub fn is_leaf(pos1: u64) -> bool {
|
||||||
if pos == 0 {
|
if pos1 == 0 {
|
||||||
return false;
|
panic!("is_leaf called with pos1 == 0");
|
||||||
}
|
}
|
||||||
bintree_postorder_height(pos) == 0
|
bintree_postorder_height(pos1) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the positions of the parent and sibling of the node at the
|
/// Calculates the positions of the parent and sibling of the node at the
|
||||||
/// provided position.
|
/// provided position.
|
||||||
pub fn family(pos: u64) -> (u64, u64) {
|
pub fn family(pos1: u64) -> (u64, u64) {
|
||||||
let (peak_map, height) = peak_map_height(pos - 1);
|
if pos1 == 0 {
|
||||||
|
panic!("family called with pos1 == 0");
|
||||||
|
}
|
||||||
|
let (peak_map, height) = peak_map_height(pos1 - 1);
|
||||||
let peak = 1 << height;
|
let peak = 1 << height;
|
||||||
if (peak_map & peak) != 0 {
|
if (peak_map & peak) != 0 {
|
||||||
(pos + 1, pos + 1 - 2 * peak)
|
(pos1 + 1, pos1 + 1 - 2 * peak)
|
||||||
} else {
|
} else {
|
||||||
(pos + 2 * peak, pos + 2 * peak - 1)
|
(pos1 + 2 * peak, pos1 + 2 * peak - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the node at this pos the "left" sibling of its parent?
|
/// Is the node at this pos the "left" sibling of its parent?
|
||||||
pub fn is_left_sibling(pos: u64) -> bool {
|
pub fn is_left_sibling(pos1: u64) -> bool {
|
||||||
let (peak_map, height) = peak_map_height(pos - 1);
|
if pos1 == 0 {
|
||||||
|
panic!("is_left_sibling called with pos1 == 0");
|
||||||
|
}
|
||||||
|
let (peak_map, height) = peak_map_height(pos1 - 1);
|
||||||
let peak = 1 << height;
|
let peak = 1 << height;
|
||||||
(peak_map & peak) == 0
|
(peak_map & peak) == 0
|
||||||
}
|
}
|
||||||
|
@ -610,8 +599,8 @@ pub fn is_left_sibling(pos: u64) -> bool {
|
||||||
/// corresponding peak in the MMR.
|
/// corresponding peak in the MMR.
|
||||||
/// The size (and therefore the set of peaks) of the MMR
|
/// The size (and therefore the set of peaks) of the MMR
|
||||||
/// is defined by last_pos.
|
/// is defined by last_pos.
|
||||||
pub fn path(pos: u64, last_pos: u64) -> impl Iterator<Item = u64> {
|
pub fn path(pos1: u64, last_pos: u64) -> impl Iterator<Item = u64> {
|
||||||
Path::new(pos, last_pos)
|
Path::new(pos1, last_pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Path {
|
struct Path {
|
||||||
|
@ -622,10 +611,13 @@ struct Path {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Path {
|
impl Path {
|
||||||
fn new(pos: u64, last_pos: u64) -> Self {
|
fn new(pos1: u64, last_pos: u64) -> Self {
|
||||||
let (peak_map, height) = peak_map_height(pos - 1);
|
if pos1 == 0 {
|
||||||
|
panic!("Path::new called with pos1 == 0");
|
||||||
|
}
|
||||||
|
let (peak_map, height) = peak_map_height(pos1 - 1);
|
||||||
Path {
|
Path {
|
||||||
current: pos,
|
current: pos1,
|
||||||
peak: 1 << height,
|
peak: 1 << height,
|
||||||
peak_map,
|
peak_map,
|
||||||
last_pos,
|
last_pos,
|
||||||
|
@ -655,13 +647,16 @@ impl Iterator for Path {
|
||||||
/// For a given starting position calculate the parent and sibling positions
|
/// For a given starting position calculate the parent and sibling positions
|
||||||
/// for the branch/path from that position to the peak of the tree.
|
/// for the branch/path from that position to the peak of the tree.
|
||||||
/// We will use the sibling positions to generate the "path" of a Merkle proof.
|
/// We will use the sibling positions to generate the "path" of a Merkle proof.
|
||||||
pub fn family_branch(pos: u64, last_pos: u64) -> Vec<(u64, u64)> {
|
pub fn family_branch(pos1: u64, last_pos: u64) -> Vec<(u64, u64)> {
|
||||||
|
if pos1 == 0 {
|
||||||
|
panic!("family_branch called with pos1 == 0");
|
||||||
|
}
|
||||||
// loop going up the tree, from node to parent, as long as we stay inside
|
// loop going up the tree, from node to parent, as long as we stay inside
|
||||||
// the tree (as defined by last_pos).
|
// the tree (as defined by last_pos).
|
||||||
let (peak_map, height) = peak_map_height(pos - 1);
|
let (peak_map, height) = peak_map_height(pos1 - 1);
|
||||||
let mut peak = 1 << height;
|
let mut peak = 1 << height;
|
||||||
let mut branch = vec![];
|
let mut branch = vec![];
|
||||||
let mut current = pos;
|
let mut current = pos1;
|
||||||
let mut sibling;
|
let mut sibling;
|
||||||
while current < last_pos {
|
while current < last_pos {
|
||||||
if (peak_map & peak) != 0 {
|
if (peak_map & peak) != 0 {
|
||||||
|
@ -708,13 +703,13 @@ pub fn bintree_leaf_pos_iter(pos1: u64) -> Box<dyn Iterator<Item = u64>> {
|
||||||
|
|
||||||
/// Iterator over all pos beneath the provided subtree root (including the root itself).
|
/// Iterator over all pos beneath the provided subtree root (including the root itself).
|
||||||
pub fn bintree_pos_iter(pos1: u64) -> impl Iterator<Item = u64> {
|
pub fn bintree_pos_iter(pos1: u64) -> impl Iterator<Item = u64> {
|
||||||
let leaf_start = max(1, bintree_leftmost(pos1 as u64));
|
let leaf_start = bintree_leftmost(pos1 as u64);
|
||||||
(leaf_start..=pos1).into_iter()
|
(leaf_start..=pos1).into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All pos in the subtree beneath the provided root, including root itself.
|
/// All pos in the subtree beneath the provided root, including root itself.
|
||||||
pub fn bintree_range(num: u64) -> Range<u64> {
|
pub fn bintree_range(pos1: u64) -> Range<u64> {
|
||||||
let height = bintree_postorder_height(num);
|
let height = bintree_postorder_height(pos1);
|
||||||
let leftmost = num + 2 - (2 << height);
|
let leftmost = pos1 + 2 - (2 << height);
|
||||||
leftmost..(num + 1)
|
leftmost..(pos1 + 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,6 @@ fn first_100_mmr_heights() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bintree_range() {
|
fn test_bintree_range() {
|
||||||
assert_eq!(pmmr::bintree_range(0), 0..1);
|
|
||||||
assert_eq!(pmmr::bintree_range(1), 1..2);
|
assert_eq!(pmmr::bintree_range(1), 1..2);
|
||||||
assert_eq!(pmmr::bintree_range(2), 2..3);
|
assert_eq!(pmmr::bintree_range(2), 2..3);
|
||||||
assert_eq!(pmmr::bintree_range(3), 1..4);
|
assert_eq!(pmmr::bintree_range(3), 1..4);
|
||||||
|
@ -104,7 +103,6 @@ fn test_bintree_range() {
|
||||||
// The pos of the rightmost leaf for the provided MMR size (last leaf in subtree).
|
// The pos of the rightmost leaf for the provided MMR size (last leaf in subtree).
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bintree_rightmost() {
|
fn test_bintree_rightmost() {
|
||||||
assert_eq!(pmmr::bintree_rightmost(0), 0);
|
|
||||||
assert_eq!(pmmr::bintree_rightmost(1), 1);
|
assert_eq!(pmmr::bintree_rightmost(1), 1);
|
||||||
assert_eq!(pmmr::bintree_rightmost(2), 2);
|
assert_eq!(pmmr::bintree_rightmost(2), 2);
|
||||||
assert_eq!(pmmr::bintree_rightmost(3), 2);
|
assert_eq!(pmmr::bintree_rightmost(3), 2);
|
||||||
|
@ -117,7 +115,6 @@ fn test_bintree_rightmost() {
|
||||||
// The pos of the leftmost leaf for the provided MMR size (first leaf in subtree).
|
// The pos of the leftmost leaf for the provided MMR size (first leaf in subtree).
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bintree_leftmost() {
|
fn test_bintree_leftmost() {
|
||||||
assert_eq!(pmmr::bintree_leftmost(0), 0);
|
|
||||||
assert_eq!(pmmr::bintree_leftmost(1), 1);
|
assert_eq!(pmmr::bintree_leftmost(1), 1);
|
||||||
assert_eq!(pmmr::bintree_leftmost(2), 2);
|
assert_eq!(pmmr::bintree_leftmost(2), 2);
|
||||||
assert_eq!(pmmr::bintree_leftmost(3), 1);
|
assert_eq!(pmmr::bintree_leftmost(3), 1);
|
||||||
|
@ -129,7 +126,6 @@ fn test_bintree_leftmost() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bintree_leaf_pos_iter() {
|
fn test_bintree_leaf_pos_iter() {
|
||||||
assert_eq!(pmmr::bintree_leaf_pos_iter(0).count(), 0);
|
|
||||||
assert_eq!(pmmr::bintree_leaf_pos_iter(1).collect::<Vec<_>>(), [1]);
|
assert_eq!(pmmr::bintree_leaf_pos_iter(1).collect::<Vec<_>>(), [1]);
|
||||||
assert_eq!(pmmr::bintree_leaf_pos_iter(2).collect::<Vec<_>>(), [2]);
|
assert_eq!(pmmr::bintree_leaf_pos_iter(2).collect::<Vec<_>>(), [2]);
|
||||||
assert_eq!(pmmr::bintree_leaf_pos_iter(3).collect::<Vec<_>>(), [1, 2]);
|
assert_eq!(pmmr::bintree_leaf_pos_iter(3).collect::<Vec<_>>(), [1, 2]);
|
||||||
|
@ -144,7 +140,6 @@ fn test_bintree_leaf_pos_iter() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bintree_pos_iter() {
|
fn test_bintree_pos_iter() {
|
||||||
assert_eq!(pmmr::bintree_pos_iter(0).count(), 0);
|
|
||||||
assert_eq!(pmmr::bintree_pos_iter(1).collect::<Vec<_>>(), [1]);
|
assert_eq!(pmmr::bintree_pos_iter(1).collect::<Vec<_>>(), [1]);
|
||||||
assert_eq!(pmmr::bintree_pos_iter(2).collect::<Vec<_>>(), [2]);
|
assert_eq!(pmmr::bintree_pos_iter(2).collect::<Vec<_>>(), [2]);
|
||||||
assert_eq!(pmmr::bintree_pos_iter(3).collect::<Vec<_>>(), [1, 2, 3]);
|
assert_eq!(pmmr::bintree_pos_iter(3).collect::<Vec<_>>(), [1, 2, 3]);
|
||||||
|
@ -159,7 +154,6 @@ fn test_bintree_pos_iter() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_leaf() {
|
fn test_is_leaf() {
|
||||||
assert_eq!(pmmr::is_leaf(0), false);
|
|
||||||
assert_eq!(pmmr::is_leaf(1), true);
|
assert_eq!(pmmr::is_leaf(1), true);
|
||||||
assert_eq!(pmmr::is_leaf(2), true);
|
assert_eq!(pmmr::is_leaf(2), true);
|
||||||
assert_eq!(pmmr::is_leaf(3), false);
|
assert_eq!(pmmr::is_leaf(3), false);
|
||||||
|
|
Loading…
Reference in a new issue