Refactor PMMR read methods into trait (#3454)

This commit is contained in:
jaspervdm 2020-09-29 15:57:33 +02:00 committed by GitHub
parent defc714f6e
commit 0aec8b533b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 273 additions and 268 deletions

View file

@ -19,7 +19,7 @@ use bit_vec::BitVec;
use croaring::Bitmap;
use crate::core::core::hash::{DefaultHashable, Hash};
use crate::core::core::pmmr::{self, ReadonlyPMMR, VecBackend, PMMR};
use crate::core::core::pmmr::{self, ReadablePMMR, ReadonlyPMMR, VecBackend, PMMR};
use crate::core::ser::{self, PMMRable, Readable, Reader, Writeable, Writer};
use crate::error::{Error, ErrorKind};
@ -176,7 +176,9 @@ impl BitmapAccumulator {
/// The root hash of the bitmap accumulator MMR.
pub fn root(&self) -> Hash {
ReadonlyPMMR::at(&self.backend, self.backend.size()).root()
ReadonlyPMMR::at(&self.backend, self.backend.size())
.root()
.expect("no root, invalid tree")
}
}

View file

@ -19,7 +19,7 @@ use crate::core::consensus::WEEK_HEIGHT;
use crate::core::core::committed::Committed;
use crate::core::core::hash::{Hash, Hashed};
use crate::core::core::merkle_proof::MerkleProof;
use crate::core::core::pmmr::{self, Backend, ReadonlyPMMR, RewindablePMMR, PMMR};
use crate::core::core::pmmr::{self, Backend, ReadablePMMR, ReadonlyPMMR, RewindablePMMR, PMMR};
use crate::core::core::{Block, BlockHeader, KernelFeatures, Output, OutputIdentifier, TxKernel};
use crate::core::global;
use crate::core::ser::{PMMRable, ProtocolVersion};
@ -369,11 +369,11 @@ impl TxHashSet {
TxHashSetRoots {
output_roots: OutputRoots {
pmmr_root: output_pmmr.root(),
pmmr_root: output_pmmr.root().expect("no root, invalid tree"),
bitmap_root: self.bitmap_accumulator.root(),
},
rproof_root: rproof_pmmr.root(),
kernel_root: kernel_pmmr.root(),
rproof_root: rproof_pmmr.root().expect("no root, invalid tree"),
kernel_root: kernel_pmmr.root().expect("no root, invalid tree"),
}
}

View file

@ -15,7 +15,7 @@
//! Lightweight readonly view into output MMR for convenience.
use crate::core::core::hash::{Hash, Hashed};
use crate::core::core::pmmr::{self, ReadonlyPMMR};
use crate::core::core::pmmr::{self, ReadablePMMR, ReadonlyPMMR};
use crate::core::core::{Block, BlockHeader, Inputs, Output, OutputIdentifier, Transaction};
use crate::core::global;
use crate::error::{Error, ErrorKind};

View file

@ -26,6 +26,138 @@ 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
pub trait ReadablePMMR {
/// Leaf type
type Item;
/// Get the hash at provided position in the MMR.
fn get_hash(&self, pos: u64) -> Option<Hash>;
/// Get the data element at provided position in the MMR.
fn get_data(&self, pos: u64) -> Option<Self::Item>;
/// Get the hash from the underlying MMR file (ignores the remove log).
fn get_from_file(&self, pos: u64) -> Option<Hash>;
/// Total size of the tree, including intermediary nodes and ignoring any pruning.
fn unpruned_size(&self) -> u64;
/// Iterator over current (unpruned, unremoved) leaf positions.
fn leaf_pos_iter(&self) -> Box<dyn Iterator<Item = u64> + '_>;
/// Iterator over current (unpruned, unremoved) leaf insertion indices.
fn leaf_idx_iter(&self, from_idx: u64) -> Box<dyn Iterator<Item = u64> + '_>;
/// Number of leaves in the MMR
fn n_unpruned_leaves(&self) -> u64;
/// Is the MMR empty?
fn is_empty(&self) -> bool {
self.unpruned_size() == 0
}
/// Takes a single peak position and hashes together
/// all the peaks to the right of this peak (if any).
/// If this return a hash then this is our peaks sibling.
/// If none then the sibling of our peak is the peak to the left.
fn bag_the_rhs(&self, peak_pos: u64) -> Option<Hash> {
let last_pos = self.unpruned_size();
let rhs = peaks(last_pos)
.into_iter()
.filter(|&x| x > peak_pos)
.filter_map(|x| self.get_from_file(x));
let mut res = None;
for peak in rhs.rev() {
res = match res {
None => Some(peak),
Some(rhash) => Some((peak, rhash).hash_with_index(last_pos)),
}
}
res
}
/// Returns a vec of the peaks of this MMR.
fn peaks(&self) -> Vec<Hash> {
peaks(self.unpruned_size())
.into_iter()
.filter_map(move |pi| {
// here we want to get from underlying hash file
// as the pos *may* have been "removed"
self.get_from_file(pi)
})
.collect()
}
/// Hashes of the peaks excluding `peak_pos`, where the rhs is bagged together
fn peak_path(&self, peak_pos: u64) -> Vec<Hash> {
let rhs = self.bag_the_rhs(peak_pos);
let mut res = peaks(self.unpruned_size())
.into_iter()
.filter(|&x| x < peak_pos)
.filter_map(|x| self.get_from_file(x))
.collect::<Vec<_>>();
if let Some(rhs) = rhs {
res.push(rhs);
}
res.reverse();
res
}
/// Computes the root of the MMR. Find all the peaks in the current
/// tree and "bags" them to get a single peak.
fn root(&self) -> Result<Hash, String> {
if self.is_empty() {
return Ok(ZERO_HASH);
}
let mut res = None;
let peaks = self.peaks();
for peak in peaks.into_iter().rev() {
res = match res {
None => Some(peak),
Some(rhash) => Some((peak, rhash).hash_with_index(self.unpruned_size())),
}
}
res.ok_or_else(|| "no root, invalid tree".to_owned())
}
/// Build a Merkle proof for the element at the given position.
fn merkle_proof(&self, pos: u64) -> Result<MerkleProof, String> {
let last_pos = self.unpruned_size();
debug!("merkle_proof {}, last_pos {}", pos, last_pos);
// check this pos is actually a leaf in the MMR
if !is_leaf(pos) {
return Err(format!("not a leaf at pos {}", pos));
}
// check we actually have a hash in the MMR at this pos
self.get_hash(pos)
.ok_or_else(|| format!("no element at pos {}", pos))?;
let family_branch = family_branch(pos, last_pos);
let mut path = family_branch
.iter()
.filter_map(|x| self.get_from_file(x.1))
.collect::<Vec<_>>();
let peak_pos = match family_branch.last() {
Some(&(x, _)) => x,
None => pos,
};
path.append(&mut self.peak_path(peak_pos));
Ok(MerkleProof {
mmr_size: last_pos,
path,
})
}
}
/// Prunable Merkle Mountain Range implementation. All positions within the tree
/// start at 1 as they're postorder tree traversal positions rather than array
/// indices.
@ -74,114 +206,6 @@ where
ReadonlyPMMR::at(&self.backend, self.last_pos)
}
/// Iterator over current (unpruned, unremoved) leaf positions.
pub fn leaf_pos_iter(&self) -> impl Iterator<Item = u64> + '_ {
self.backend.leaf_pos_iter()
}
/// Number of leafs in the MMR
pub fn n_unpruned_leaves(&self) -> u64 {
self.backend.n_unpruned_leaves()
}
/// Iterator over current (unpruned, unremoved) leaf insertion indices.
pub fn leaf_idx_iter(&self, from_idx: u64) -> impl Iterator<Item = u64> + '_ {
self.backend.leaf_idx_iter(from_idx)
}
/// Returns a vec of the peaks of this MMR.
pub fn peaks(&self) -> impl DoubleEndedIterator<Item = Hash> + '_ {
let peaks_pos = peaks(self.last_pos);
peaks_pos.into_iter().filter_map(move |pi| {
// here we want to get from underlying hash file
// as the pos *may* have been "removed"
self.backend.get_from_file(pi)
})
}
fn peak_path(&self, peak_pos: u64) -> Vec<Hash> {
let rhs = self.bag_the_rhs(peak_pos);
let mut res = peaks(self.last_pos)
.into_iter()
.filter(|x| *x < peak_pos)
.filter_map(|x| self.backend.get_from_file(x))
.collect::<Vec<_>>();
if let Some(rhs) = rhs {
res.push(rhs);
}
res.reverse();
res
}
/// Takes a single peak position and hashes together
/// all the peaks to the right of this peak (if any).
/// If this return a hash then this is our peaks sibling.
/// If none then the sibling of our peak is the peak to the left.
pub fn bag_the_rhs(&self, peak_pos: u64) -> Option<Hash> {
let rhs = peaks(self.last_pos)
.into_iter()
.filter(|x| *x > peak_pos)
.filter_map(|x| self.backend.get_from_file(x));
let mut res = None;
for peak in rhs.rev() {
res = match res {
None => Some(peak),
Some(rhash) => Some((peak, rhash).hash_with_index(self.unpruned_size())),
}
}
res
}
/// Computes the root of the MMR. Find all the peaks in the current
/// tree and "bags" them to get a single peak.
pub fn root(&self) -> Result<Hash, String> {
if self.is_empty() {
return Ok(ZERO_HASH);
}
let mut res = None;
for peak in self.peaks().rev() {
res = match res {
None => Some(peak),
Some(rhash) => Some((peak, rhash).hash_with_index(self.unpruned_size())),
}
}
res.ok_or_else(|| "no root, invalid tree".to_owned())
}
/// Build a Merkle proof for the element at the given position.
pub fn merkle_proof(&self, pos: u64) -> Result<MerkleProof, String> {
debug!("merkle_proof {}, last_pos {}", pos, self.last_pos);
// check this pos is actually a leaf in the MMR
if !is_leaf(pos) {
return Err(format!("not a leaf at pos {}", pos));
}
// check we actually have a hash in the MMR at this pos
self.get_hash(pos)
.ok_or_else(|| format!("no element at pos {}", pos))?;
let mmr_size = self.unpruned_size();
let family_branch = family_branch(pos, self.last_pos);
let mut path = family_branch
.iter()
.filter_map(|x| self.get_from_file(x.1))
.collect::<Vec<_>>();
let peak_pos = match family_branch.last() {
Some(&(x, _)) => x,
None => pos,
};
path.append(&mut self.peak_path(peak_pos));
Ok(MerkleProof { mmr_size, path })
}
/// Push a new element into the MMR. Computes new related peaks at
/// the same time if applicable.
pub fn push(&mut self, elmt: &T) -> Result<u64, String> {
@ -258,43 +282,6 @@ where
Ok(true)
}
/// Get the hash at provided position in the MMR.
pub fn get_hash(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else if is_leaf(pos) {
// If we are a leaf then get hash from the backend.
self.backend.get_hash(pos)
} else {
// If we are not a leaf get hash ignoring the remove log.
self.backend.get_from_file(pos)
}
}
/// Get the data element at provided position in the MMR.
pub fn get_data(&self, pos: u64) -> Option<T::E> {
if pos > self.last_pos {
// If we are beyond the rhs of the MMR return None.
None
} else if is_leaf(pos) {
// If we are a leaf then get data from the backend.
self.backend.get_data(pos)
} else {
// If we are not a leaf then return None as only leaves have data.
None
}
}
/// Get the hash from the underlying MMR file
/// (ignores the remove log).
fn get_from_file(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else {
self.backend.get_from_file(pos)
}
}
/// Walks all unpruned nodes in the MMR and revalidate all parent hashes
pub fn validate(&self) -> Result<(), String> {
// iterate on all parent nodes
@ -323,17 +310,6 @@ where
Ok(())
}
/// Is the MMR empty?
pub fn is_empty(&self) -> bool {
self.last_pos == 0
}
/// Total size of the tree, including intermediary nodes and ignoring any
/// pruning.
pub fn unpruned_size(&self) -> u64 {
self.last_pos
}
/// Debugging utility to print information about the MMRs. Short version
/// only prints the last 8 nodes.
pub fn dump(&self, short: bool) {
@ -396,6 +372,63 @@ where
}
}
impl<'a, T, B> ReadablePMMR for PMMR<'a, T, B>
where
T: PMMRable,
B: 'a + Backend<T>,
{
type Item = T::E;
fn get_hash(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else if is_leaf(pos) {
// If we are a leaf then get hash from the backend.
self.backend.get_hash(pos)
} else {
// If we are not a leaf get hash ignoring the remove log.
self.backend.get_from_file(pos)
}
}
fn get_data(&self, pos: u64) -> Option<Self::Item> {
if pos > self.last_pos {
// If we are beyond the rhs of the MMR return None.
None
} else if is_leaf(pos) {
// If we are a leaf then get data from the backend.
self.backend.get_data(pos)
} else {
// If we are not a leaf then return None as only leaves have data.
None
}
}
fn get_from_file(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else {
self.backend.get_from_file(pos)
}
}
fn unpruned_size(&self) -> u64 {
self.last_pos
}
fn leaf_pos_iter(&self) -> Box<dyn Iterator<Item = u64> + '_> {
self.backend.leaf_pos_iter()
}
fn leaf_idx_iter(&self, from_idx: u64) -> Box<dyn Iterator<Item = u64> + '_> {
self.backend.leaf_idx_iter(from_idx)
}
fn n_unpruned_leaves(&self) -> u64 {
self.backend.n_unpruned_leaves()
}
}
/// 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
/// side of the range, and navigates toward lower siblings toward the right

View file

@ -16,10 +16,10 @@
use std::marker;
use crate::core::hash::{Hash, ZERO_HASH};
use crate::core::pmmr::pmmr::{bintree_rightmost, peaks};
use crate::core::hash::Hash;
use crate::core::pmmr::pmmr::{bintree_rightmost, ReadablePMMR};
use crate::core::pmmr::{is_leaf, Backend};
use crate::ser::{PMMRIndexHashable, PMMRable};
use crate::ser::PMMRable;
/// Readonly view of a PMMR.
pub struct ReadonlyPMMR<'a, T, B>
@ -59,93 +59,6 @@ where
}
}
/// Get the data element at provided position in the MMR.
pub fn get_data(&self, pos: u64) -> Option<T::E> {
if pos > self.last_pos {
// If we are beyond the rhs of the MMR return None.
None
} else if is_leaf(pos) {
// If we are a leaf then get data from the backend.
self.backend.get_data(pos)
} else {
// If we are not a leaf then return None as only leaves have data.
None
}
}
/// Get the hash at provided position in the MMR.
pub fn get_hash(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else if is_leaf(pos) {
// If we are a leaf then get hash from the backend.
self.backend.get_hash(pos)
} else {
// If we are not a leaf get hash ignoring the remove log.
self.backend.get_from_file(pos)
}
}
/// Get the hash from the underlying MMR file, ignoring the leafset.
/// Some entries may have been removed from the leafset but not yet pruned from the file.
pub fn get_from_file(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else {
self.backend.get_from_file(pos)
}
}
/// Iterator over current (unpruned, unremoved) leaf positions.
pub fn leaf_pos_iter(&self) -> impl Iterator<Item = u64> + '_ {
self.backend.leaf_pos_iter()
}
/// Iterator over current (unpruned, unremoved) leaf insertion indices.
pub fn leaf_idx_iter(&self, from_idx: u64) -> impl Iterator<Item = u64> + '_ {
self.backend.leaf_idx_iter(from_idx)
}
/// Is the MMR empty?
pub fn is_empty(&self) -> bool {
self.last_pos == 0
}
/// Computes the root of the MMR. Find all the peaks in the current
/// tree and "bags" them to get a single peak.
pub fn root(&self) -> Hash {
if self.is_empty() {
return ZERO_HASH;
}
let mut res = None;
for peak in self.peaks().iter().rev() {
res = match res {
None => Some(*peak),
Some(rhash) => Some((*peak, rhash).hash_with_index(self.unpruned_size())),
}
}
res.expect("no root, invalid tree")
}
/// Returns a vec of the peaks of this MMR.
pub fn peaks(&self) -> Vec<Hash> {
let peaks_pos = peaks(self.last_pos);
peaks_pos
.into_iter()
.filter_map(|pi| {
// here we want to get from underlying hash file
// as the pos *may* have been "removed"
self.backend.get_from_file(pi)
})
.collect()
}
/// Total size of the tree, including intermediary nodes and ignoring any
/// pruning.
pub fn unpruned_size(&self) -> u64 {
self.last_pos
}
/// Helper function which returns un-pruned nodes from the insertion index
/// forward
/// returns last pmmr index returned along with data
@ -194,3 +107,60 @@ where
return_vec
}
}
impl<'a, T, B> ReadablePMMR for ReadonlyPMMR<'a, T, B>
where
T: PMMRable,
B: 'a + Backend<T>,
{
type Item = T::E;
fn get_hash(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else if is_leaf(pos) {
// If we are a leaf then get hash from the backend.
self.backend.get_hash(pos)
} else {
// If we are not a leaf get hash ignoring the remove log.
self.backend.get_from_file(pos)
}
}
fn get_data(&self, pos: u64) -> Option<Self::Item> {
if pos > self.last_pos {
// If we are beyond the rhs of the MMR return None.
None
} else if is_leaf(pos) {
// If we are a leaf then get data from the backend.
self.backend.get_data(pos)
} else {
// If we are not a leaf then return None as only leaves have data.
None
}
}
fn get_from_file(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else {
self.backend.get_from_file(pos)
}
}
fn unpruned_size(&self) -> u64 {
self.last_pos
}
fn leaf_pos_iter(&self) -> Box<dyn Iterator<Item = u64> + '_> {
self.backend.leaf_pos_iter()
}
fn leaf_idx_iter(&self, from_idx: u64) -> Box<dyn Iterator<Item = u64> + '_> {
self.backend.leaf_idx_iter(from_idx)
}
fn n_unpruned_leaves(&self) -> u64 {
self.backend.n_unpruned_leaves()
}
}

View file

@ -15,7 +15,7 @@
mod common;
use self::core::core::merkle_proof::MerkleProof;
use self::core::core::pmmr::{VecBackend, PMMR};
use self::core::core::pmmr::{ReadablePMMR, VecBackend, PMMR};
use self::core::ser::{self, PMMRIndexHashable};
use crate::common::TestElem;
use grin_core as core;
@ -90,7 +90,7 @@ fn pmmr_merkle_proof() {
assert_eq!(pmmr.get_hash(3).unwrap(), pos_2);
assert_eq!(pmmr.root().unwrap(), pos_2);
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), [pos_2]);
assert_eq!(pmmr.peaks(), vec![pos_2]);
// single peak, path with single sibling
let proof = pmmr.merkle_proof(1).unwrap();
@ -107,7 +107,7 @@ fn pmmr_merkle_proof() {
assert_eq!(pmmr.get_hash(4).unwrap(), pos_3);
assert_eq!(pmmr.root().unwrap(), (pos_2, pos_3).hash_with_index(4));
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), [pos_2, pos_3]);
assert_eq!(pmmr.peaks(), vec![pos_2, pos_3]);
let proof = pmmr.merkle_proof(1).unwrap();
assert_eq!(proof.path, vec![pos_1, pos_3]);

View file

@ -15,7 +15,7 @@
mod common;
use self::core::core::hash::Hash;
use self::core::core::pmmr::{self, VecBackend, PMMR};
use self::core::core::pmmr::{self, ReadablePMMR, VecBackend, PMMR};
use self::core::ser::PMMRIndexHashable;
use crate::common::TestElem;
use chrono::prelude::Utc;
@ -278,7 +278,7 @@ fn pmmr_push_root() {
pmmr.push(&elems[0]).unwrap();
pmmr.dump(false);
let pos_0 = elems[0].hash_with_index(0);
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), vec![pos_0]);
assert_eq!(pmmr.peaks(), vec![pos_0]);
assert_eq!(pmmr.root().unwrap(), pos_0);
assert_eq!(pmmr.unpruned_size(), 1);
@ -287,7 +287,7 @@ fn pmmr_push_root() {
pmmr.dump(false);
let pos_1 = elems[1].hash_with_index(1);
let pos_2 = (pos_0, pos_1).hash_with_index(2);
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), vec![pos_2]);
assert_eq!(pmmr.peaks(), vec![pos_2]);
assert_eq!(pmmr.root().unwrap(), pos_2);
assert_eq!(pmmr.unpruned_size(), 3);
@ -295,7 +295,7 @@ fn pmmr_push_root() {
pmmr.push(&elems[2]).unwrap();
pmmr.dump(false);
let pos_3 = elems[2].hash_with_index(3);
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), vec![pos_2, pos_3]);
assert_eq!(pmmr.peaks(), vec![pos_2, pos_3]);
assert_eq!(pmmr.root().unwrap(), (pos_2, pos_3).hash_with_index(4));
assert_eq!(pmmr.unpruned_size(), 4);
@ -305,7 +305,7 @@ fn pmmr_push_root() {
let pos_4 = elems[3].hash_with_index(4);
let pos_5 = (pos_3, pos_4).hash_with_index(5);
let pos_6 = (pos_2, pos_5).hash_with_index(6);
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), vec![pos_6]);
assert_eq!(pmmr.peaks(), vec![pos_6]);
assert_eq!(pmmr.root().unwrap(), pos_6);
assert_eq!(pmmr.unpruned_size(), 7);
@ -313,7 +313,7 @@ fn pmmr_push_root() {
pmmr.push(&elems[4]).unwrap();
pmmr.dump(false);
let pos_7 = elems[4].hash_with_index(7);
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), vec![pos_6, pos_7]);
assert_eq!(pmmr.peaks(), vec![pos_6, pos_7]);
assert_eq!(pmmr.root().unwrap(), (pos_6, pos_7).hash_with_index(8));
assert_eq!(pmmr.unpruned_size(), 8);
@ -321,14 +321,14 @@ fn pmmr_push_root() {
pmmr.push(&elems[5]).unwrap();
let pos_8 = elems[5].hash_with_index(8);
let pos_9 = (pos_7, pos_8).hash_with_index(9);
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), vec![pos_6, pos_9]);
assert_eq!(pmmr.peaks(), vec![pos_6, pos_9]);
assert_eq!(pmmr.root().unwrap(), (pos_6, pos_9).hash_with_index(10));
assert_eq!(pmmr.unpruned_size(), 10);
// seven elements
pmmr.push(&elems[6]).unwrap();
let pos_10 = elems[6].hash_with_index(10);
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), vec![pos_6, pos_9, pos_10]);
assert_eq!(pmmr.peaks(), vec![pos_6, pos_9, pos_10]);
assert_eq!(
pmmr.root().unwrap(),
(pos_6, (pos_9, pos_10).hash_with_index(11)).hash_with_index(11)
@ -342,14 +342,14 @@ fn pmmr_push_root() {
let pos_12 = (pos_10, pos_11).hash_with_index(12);
let pos_13 = (pos_9, pos_12).hash_with_index(13);
let pos_14 = (pos_6, pos_13).hash_with_index(14);
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), vec![pos_14]);
assert_eq!(pmmr.peaks(), vec![pos_14]);
assert_eq!(pmmr.root().unwrap(), pos_14);
assert_eq!(pmmr.unpruned_size(), 15);
// nine elements
pmmr.push(&elems[8]).unwrap();
let pos_15 = elems[8].hash_with_index(15);
assert_eq!(pmmr.peaks().collect::<Vec<_>>(), vec![pos_14, pos_15]);
assert_eq!(pmmr.peaks(), vec![pos_14, pos_15]);
assert_eq!(pmmr.root().unwrap(), (pos_14, pos_15).hash_with_index(16));
assert_eq!(pmmr.unpruned_size(), 16);
}

View file

@ -14,7 +14,7 @@
mod common;
use self::core::core::pmmr::{VecBackend, PMMR};
use self::core::core::pmmr::{ReadablePMMR, VecBackend, PMMR};
use crate::common::TestElem;
use grin_core as core;

View file

@ -22,7 +22,7 @@ use chrono::prelude::Utc;
use croaring::Bitmap;
use crate::core::core::hash::DefaultHashable;
use crate::core::core::pmmr::{Backend, PMMR};
use crate::core::core::pmmr::{Backend, ReadablePMMR, PMMR};
use crate::core::ser::{
Error, PMMRIndexHashable, PMMRable, ProtocolVersion, Readable, Reader, Writeable, Writer,
};